Index: linux-2.6.33/drivers/spi/Kconfig
===================================================================
--- linux-2.6.33.orig/drivers/spi/Kconfig
+++ linux-2.6.33/drivers/spi/Kconfig
@@ -335,6 +335,10 @@ config SPI_DW_PCI
 #
 comment "SPI Protocol Masters"
 
+config SPI_MRST_GTM501
+	tristate "SPI protocol driver for GTM501l"
+	depends on SPI_MRST
+
 config SPI_SPIDEV
 	tristate "User mode SPI device driver support"
 	depends on EXPERIMENTAL
Index: linux-2.6.33/drivers/spi/Makefile
===================================================================
--- linux-2.6.33.orig/drivers/spi/Makefile
+++ linux-2.6.33/drivers/spi/Makefile
@@ -43,6 +43,7 @@ obj-$(CONFIG_SPI_SH_MSIOF)		+= spi_sh_ms
 obj-$(CONFIG_SPI_STMP3XXX)		+= spi_stmp.o
 obj-$(CONFIG_SPI_NUC900)		+= spi_nuc900.o
 obj-$(CONFIG_SPI_MRST)			+= mrst_spi.o
+obj-$(CONFIG_SPI_MRST_GTM501)		+= gtm501l_spi.o
 
 # special build for s3c24xx spi driver with fiq support
 spi_s3c24xx_hw-y			:= spi_s3c24xx.o
Index: linux-2.6.33/drivers/spi/gtm501l_spi.c
===================================================================
--- /dev/null
+++ linux-2.6.33/drivers/spi/gtm501l_spi.c
@@ -0,0 +1,2029 @@
+/****************************************************************************
+ *
+ * Driver for the Option GTM501L spi modem.
+ *
+ * Copyright (C) 2008 Option International
+ * Copyright (C) 2008 Filip Aben <f.aben@option.com>
+ *                    Denis Joseph Barrow <d.barow@option.com>
+ *                    Jan Dumon <j.dumon@option.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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
+ * USA
+ *
+ *
+ *
+ *****************************************************************************/
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/tty.h>
+#include <linux/device.h>
+#include <linux/spi/spi.h>
+#include <linux/tty.h>
+#include <linux/kfifo.h>
+#include <linux/tty_flip.h>
+#include <linux/workqueue.h>
+#include <linux/timer.h>
+
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/rfkill.h>
+#include <linux/netdevice.h>
+#include <linux/inetdevice.h>
+#include <linux/skbuff.h>
+#include <net/arp.h>
+#include <linux/ip.h>
+#include <linux/dmapool.h>
+#include <linux/gpio.h>
+#include <linux/sysfs.h>
+
+#include <asm/ipc_defs.h>
+
+#ifdef CONFIG_DEBUG_FS
+#include <linux/debugfs.h>
+#include <linux/ktime.h>
+#include <linux/spinlock.h>
+#endif
+
+#include "gtm501l_spi.h"
+#include <linux/spi/mrst_spi.h>
+
+/* various static variables */
+static struct tty_driver *tty_drv;
+static struct ktermios *gtm501l_termios[GTM501L_MAX_MINORS];
+static struct ktermios *gtm501l_termios_locked[GTM501L_MAX_MINORS];
+static struct lock_class_key gtm501l_key;
+static struct gtm501l_port_data *gtm501l_serial_ports[GTM501L_MAX_MINORS];
+
+/* Default port spec */
+static struct gtm501l_port_spec gtm501l_default_port_spec[] = {
+	{ 1, GTM501L_PORT_SPEC_SERIAL, "Control" },	/*  0 */
+	{ 1, GTM501L_PORT_SPEC_SERIAL, "NetAT" },	/*  1 */
+	{ 1, GTM501L_PORT_SPEC_NET,    "NetIP" },	/*  2 */
+	{ 1, GTM501L_PORT_SPEC_SERIAL, "App" },		/*  3 */
+	{ 1, GTM501L_PORT_SPEC_SERIAL, "App2" },	/*  4 */
+	{ 1, GTM501L_PORT_SPEC_SERIAL, "PC/SC" },	/*  5 */
+	{ 1, GTM501L_PORT_SPEC_SERIAL, "Modem" },	/*  6 */
+	{ 1, GTM501L_PORT_SPEC_SERIAL, "Diag" },	/*  7 */
+	{ 1, GTM501L_PORT_SPEC_SERIAL, "Logger" },	/*  8 */
+	{ 0, GTM501L_PORT_SPEC_SERIAL, "" },		/*  9 */
+	{ 1, GTM501L_PORT_SPEC_NET,    "NetIP2" },	/* 10 */
+	{ 0, GTM501L_PORT_SPEC_SERIAL, "" },		/* 11 */
+	{ 0, GTM501L_PORT_SPEC_SERIAL, "" },		/* 12 */
+	{ 0, GTM501L_PORT_SPEC_SERIAL, "" },		/* 13 */
+	{ 0, GTM501L_PORT_SPEC_SERIAL, "" },		/* 14 */
+	{ 0, GTM501L_PORT_SPEC_SERIAL, "" },		/* 15 */
+};
+
+/* Module load parameter */
+static int gpio_in = -1;                  /* GPIO interrupt input assignment, set default to -1 */
+static int backoff_enabled = 1;		  /* Enable the backoff timer */
+static int spi_b16 = 1;                   /* Enable 16bit SPI word length, otherwise use 8bit SPI word length */
+int gtm501l_debug = 0;
+
+/* temp debug variables */
+static int spi_err_count = 0;
+static unsigned int total_tty_write = 0;
+static unsigned int total_spi_write = 0;
+
+static unsigned char scratch_buf[GTM501L_TRANSFER_SIZE];
+
+static int reset_state = 1;
+
+/* prototype declarations */
+static void gtm501l_push_skb(struct gtm501l_port_data *port_data);
+static void gtm501l_net_init(struct net_device *net);
+static void gtm501l_spi_complete(void *ctx);
+static void _gtm501l_set_termios(struct tty_struct *tty, struct ktermios *old);
+static void gtm501l_throttle(struct tty_struct *tty);
+static void gtm501l_create_ports(struct gtm501l_device *gtm_dev,
+				 struct gtm501l_port_spec *specs);
+/*static void gtm501l_pmic_init_voltages( void );*/
+static void gtm501l_pmic_set_wwanresetn( unsigned char level );
+static void gtm501l_pmic_set_wwandisablen( unsigned char level );
+
+void gtm501l_debug_printk(const char *function, int line, char *format, ...) {
+	va_list args;
+	int len;
+	char buffer[255];
+
+	len = snprintf(buffer, 255, DRVNAME " [%s:%d] ", function, line);
+
+	va_start(args, format);
+	vsnprintf(buffer + len, 255 - len, format, args);
+	va_end(args);
+
+	printk("%s", buffer);
+}
+
+#define __bswap_16(x) \
+     (__extension__							      \
+      ({ register unsigned short int __v, __x = (x);			      \
+	 __asm__ ("rorw $8, %w0"					      \
+		    : "=r" (__v)					      \
+		    : "0" (__x)						      \
+		    : "cc");						      \
+	 __v; }))
+
+static inline void swap_buf(u16 *buf, int len) {
+	int n;
+	len = (len + 1) / 2;
+	n = (len + 7) / 8;
+	switch (len % 8) {
+	case 0: do { *buf = __bswap_16(*buf); buf++;
+	case 7:      *buf = __bswap_16(*buf); buf++;
+	case 6:      *buf = __bswap_16(*buf); buf++;
+	case 5:      *buf = __bswap_16(*buf); buf++;
+	case 4:      *buf = __bswap_16(*buf); buf++;
+	case 3:      *buf = __bswap_16(*buf); buf++;
+	case 2:      *buf = __bswap_16(*buf); buf++;
+	case 1:      *buf = __bswap_16(*buf); buf++;
+		} while (--n > 0);
+	}
+}
+
+#ifdef CONFIG_DEBUG_FS
+
+static ssize_t gtm501l_read_gpio(struct file *file, char __user *user_buf, size_t count, loff_t *ppos)
+{
+	char buf[32];
+	unsigned int len = 0;
+	if (gpio_in >= 0) {
+		len += snprintf(buf+len, sizeof(buf)-len,
+				"GPIO%d = %d\n", gpio_in, (gpio_get_value(gpio_in))?1:0);
+	} else {
+		len += snprintf(buf+len, sizeof(buf)-len,
+				"GPIO unuse\n");
+	}
+	return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static struct file_operations gtm501l_gpio_operations = {
+	.owner		= THIS_MODULE,
+	.read		= gtm501l_read_gpio,
+};
+
+static ssize_t gtm501l_read_pmic(struct file *file, char __user *user_buf, size_t count, loff_t *ppos)
+{
+	char buf[8];
+	unsigned int len = sprintf(buf, "%d\n", reset_state);
+
+	return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static ssize_t gtm501l_write_pmic(struct file *file, const char __user *user, size_t count, loff_t *offset)
+{
+	reset_state = simple_strtoul(user, NULL, 0);
+	gtm501l_pmic_set_wwanresetn( reset_state );
+	gtm501l_pmic_set_wwandisablen( reset_state );
+	return count;
+}
+
+static struct file_operations gtm501l_pmic_operations = {
+	.owner		= THIS_MODULE,
+	.read		= gtm501l_read_pmic,
+	.write		= gtm501l_write_pmic,
+};
+
+/**
+ * gtm501l_debugfs_init generates all debugfs file nodes.
+ * It initialized the frame statistic structure.
+ */
+static int gtm501l_debugfs_init(struct gtm501l_device *gtm_dev)
+{
+	gtm_dev->debugfs = debugfs_create_dir("gtm501l", NULL);
+	if (!gtm_dev->debugfs)
+		return -ENOMEM;
+
+	debugfs_create_file("gpio", S_IFREG | S_IRUGO,
+		gtm_dev->debugfs, NULL, &gtm501l_gpio_operations);
+	debugfs_create_file("pmic", S_IFREG | S_IRUGO,
+		gtm_dev->debugfs, NULL, &gtm501l_pmic_operations);
+	debugfs_create_u32("backoff_timer", S_IFREG | S_IRUGO,
+		gtm_dev->debugfs, &backoff_enabled);
+	debugfs_create_x32("flags", S_IFREG | S_IRUGO,
+		gtm_dev->debugfs, (unsigned int *)&gtm_dev->flags);
+#ifdef DEBUG
+	debugfs_create_x32("debug", S_IFREG | S_IRUGO,
+		gtm_dev->debugfs, &gtm501l_debug);
+#endif
+
+	return 0;
+}
+
+static void gtm501l_debugfs_remove(struct gtm501l_device *gtm_dev)
+{
+	if (gtm_dev->debugfs)
+		debugfs_remove_recursive(gtm_dev->debugfs);
+}
+#endif
+
+static __be32 gtm501l_get_ip_addr(struct net_device *net)
+{
+	struct in_device *in_dev;
+
+	if ((in_dev = __in_dev_get_rtnl(net)) != NULL) {
+		if(in_dev->ifa_list) {
+			return in_dev->ifa_list->ifa_local;
+		}
+	}
+
+	return 0;
+}
+
+/*
+static void gtm501l_pmic_init_voltages( void )
+{
+	struct ipc_pmic_reg_data reg_data;
+	
+	reg_data.num_entries = 3;
+
+	// Set VDDQCNT
+
+	reg_data.pmic_reg_data[0].register_address = 0x037;
+	reg_data.pmic_reg_data[0].value = 0x07;
+	
+	// Set YMX3GDCNT
+	
+	reg_data.pmic_reg_data[1].register_address = 0x03c;
+	reg_data.pmic_reg_data[1].value = 0x47;
+	
+	// Set CLKOUT
+	
+	reg_data.pmic_reg_data[2].register_address = 0x021;
+	reg_data.pmic_reg_data[2].value = 0x00;
+	ipc_pmic_register_write(&reg_data, 0);
+
+}
+*/
+
+static void gtm501l_pmic_set_wwanresetn( unsigned char level )
+{
+	struct ipc_pmic_mod_reg_data mod_reg_data;
+
+	// WWAN_RESET_N is connected to COMMS_GPO5
+	
+	mod_reg_data.num_entries = 1;
+	mod_reg_data.pmic_mod_reg_data[0].bit_map = 0x20;
+	mod_reg_data.pmic_mod_reg_data[0].register_address = 0x0f4;
+
+	if (level)
+	{
+		mod_reg_data.pmic_mod_reg_data[0].value = 0x20;
+	}
+	else
+	{
+		mod_reg_data.pmic_mod_reg_data[0].value = 0x00;
+	}
+
+	ipc_pmic_register_read_modify(&mod_reg_data);
+}
+
+static void gtm501l_pmic_set_wwandisablen( unsigned char level )
+{
+	struct ipc_pmic_mod_reg_data mod_reg_data;
+
+	// WWAN_DISABLE_N is connected to COMMS_GPO0
+	
+	mod_reg_data.num_entries = 1;
+	mod_reg_data.pmic_mod_reg_data[0].bit_map = 0x01;
+	mod_reg_data.pmic_mod_reg_data[0].register_address = 0x0f4;
+
+	if (level)
+	{
+		mod_reg_data.pmic_mod_reg_data[0].value = 0x01;
+	}
+	else
+	{
+		mod_reg_data.pmic_mod_reg_data[0].value = 0x00;
+	}
+
+	ipc_pmic_register_read_modify(&mod_reg_data);
+}
+
+
+static void gtm501l_rx_netchar(struct gtm501l_net *gtm_net,
+			       unsigned char *in_buf, int size)
+{
+	struct net_device *net = gtm_net->net;
+	unsigned int temp_bytes;
+	unsigned int buffer_offset = 0, n;
+	unsigned int frame_len;
+	unsigned char *tmp_rx_buf;
+	unsigned char c;
+	int header_invalid;
+
+	while(size) {
+		switch (gtm_net->rx_state) {
+		case WAIT_IP:
+			/* waiting for IP header. */
+			/* wanted bytes - size of ip header */
+			temp_bytes = (size < gtm_net->rx_buf_missing) ?
+				size : gtm_net->rx_buf_missing;
+
+			memcpy(((unsigned char *)(&gtm_net->rx_ip_hdr)) +
+			       gtm_net->rx_buf_size, in_buf + buffer_offset,
+			       temp_bytes);
+
+			gtm_net->rx_buf_size += temp_bytes;
+			buffer_offset += temp_bytes;
+			gtm_net->rx_buf_missing -= temp_bytes;
+			size -= temp_bytes;
+
+			if (!gtm_net->rx_buf_missing) {
+				/* header is complete allocate an sk_buffer and
+				 * continue to WAIT_DATA */
+
+				header_invalid = 0;
+
+				frame_len = ntohs(gtm_net->rx_ip_hdr.tot_len);
+				if ((frame_len > GTM501L_DEFAULT_MRU) ||
+				    (frame_len < sizeof(struct iphdr))) {
+					if(!gtm_net->sync_lost)
+						dev_err(&net->dev,
+							"Invalid frame length (%d)\n",
+							frame_len);
+					header_invalid = 1;
+				}
+
+				/* Validate destination address */
+				if (!header_invalid &&
+				    (gtm501l_get_ip_addr(net) != gtm_net->rx_ip_hdr.daddr)) {
+					if(!gtm_net->sync_lost)
+						dev_err(&net->dev,
+							"Not our address (" NIPQUAD_FMT ")\n",
+							NIPQUAD(gtm_net->rx_ip_hdr.daddr));
+					header_invalid = 1;
+				}
+
+				if (header_invalid) {
+					/* This header is not valid, roll back
+					 * for sizeof(header) bytes - 1 and
+					 * wait for sync */
+					gtm_net->rx_state = WAIT_SYNC;
+					n = min(buffer_offset,
+						sizeof(struct iphdr) - 1);
+					buffer_offset -= n;
+					size += n;
+					continue;
+				}
+
+				/* Allocate an sk_buff */
+				gtm_net->rx_skb = dev_alloc_skb(frame_len);
+				if (!gtm_net->rx_skb) {
+					/* We got no receive buffer. */
+					//D1("ddcould not allocate memory");
+					gtm_net->rx_state = WAIT_SYNC;
+					return;
+				}
+
+				if(gtm_net->sync_lost) {
+					dev_info(&net->dev, "Back in sync. (%d stray bytes)\n",
+						gtm_net->sync_lost);
+					gtm_net->sync_lost = 0;
+				}
+
+				/* Here's where it came from */
+				gtm_net->rx_skb->dev = net;
+
+				/* Copy what we got so far. make room for iphdr
+				 * after tail. */
+				tmp_rx_buf =
+				    skb_put(gtm_net->rx_skb,
+					    sizeof(struct iphdr));
+				memcpy(tmp_rx_buf, (char *)&(gtm_net->rx_ip_hdr),
+				       sizeof(struct iphdr));
+
+				/* ETH_HLEN */
+				gtm_net->rx_buf_size = sizeof(struct iphdr);
+
+				gtm_net->rx_buf_missing =
+				    frame_len - sizeof(struct iphdr);
+				gtm_net->rx_state = WAIT_DATA;
+			}
+			break;
+
+		case WAIT_DATA:
+			temp_bytes = (size < gtm_net->rx_buf_missing)
+					? size : gtm_net->rx_buf_missing;
+
+			/* Copy the rest of the bytes that are left in the
+			 * buffer into the waiting sk_buf. */
+			/* Make room for temp_bytes after tail. */
+			tmp_rx_buf = skb_put(gtm_net->rx_skb, temp_bytes);
+			memcpy(tmp_rx_buf, in_buf + buffer_offset, temp_bytes);
+
+			gtm_net->rx_buf_missing -= temp_bytes;
+			size -= temp_bytes;
+			buffer_offset += temp_bytes;
+			gtm_net->rx_buf_size += temp_bytes;
+			if (!gtm_net->rx_buf_missing) {
+				/* Packet is complete. Inject into stack. */
+				/* We have IP packet here */
+				gtm_net->rx_skb->protocol =
+						__constant_htons(ETH_P_IP);
+				/* don't check it */
+				gtm_net->rx_skb->ip_summed =
+					CHECKSUM_UNNECESSARY;
+
+				skb_reset_mac_header(gtm_net->rx_skb);
+
+				/* Ship it off to the kernel */
+				netif_rx(gtm_net->rx_skb);
+				/* No longer our buffer. */
+				gtm_net->rx_skb = NULL;
+
+				/* update out statistics */
+				STATS(net).rx_packets++;
+				STATS(net).rx_bytes += gtm_net->rx_buf_size;
+
+				gtm_net->rx_buf_size = 0;
+				gtm_net->rx_buf_missing = sizeof(struct iphdr);
+				gtm_net->rx_state = WAIT_IP;
+			}
+			break;
+
+		case WAIT_SYNC:
+			if(!gtm_net->sync_lost) {
+				dev_err(&net->dev, "Lost sync...\n");
+			}
+			gtm_net->sync_lost++;
+			/* Look for the next possible IP header */
+			c = *(in_buf + buffer_offset);
+			if(c >= 0x45 && c <= 0x4f) {
+				/* This might be the begin of a new ip pkt */
+				gtm_net->rx_state = WAIT_IP;
+				gtm_net->rx_buf_size = 0;
+				gtm_net->rx_buf_missing = sizeof(struct iphdr);
+			}
+			else {
+				size--;
+				buffer_offset++;
+			}
+			break;
+
+		default:
+			size--;
+			break;
+		}
+	}
+}
+
+/* mux functions */
+
+/* function that tells you how much characters you can feed to fill your mux buffer*/
+static inline int gtm501l_mux_to_demux(int count)
+{
+	int burst_cnt, remain_cnt;
+	if (count & 0x1) {
+		printk("Error - space in frame can't be an odd number\n");
+		return -1;
+	}
+	/* We've got 2 extra bytes of framing per 512 bytes of data */
+	burst_cnt = (count / (MUX_BURST_SIZE + 2)) * MUX_BURST_SIZE;
+	remain_cnt = count % (MUX_BURST_SIZE + 2);
+
+	if (remain_cnt > 2)
+		return remain_cnt - 2 + burst_cnt;
+	else
+		return burst_cnt + 1;
+}
+
+/* multiplexes the data into the output buffer. output buffer is expected to be large enough to fit the data. */
+static int gtm501l_mux_data(int chan_id, unsigned char *in_buf, int in_size,
+			    unsigned char *out_buf)
+{
+	int odd, cnt, result_size = 0;
+
+	/* check for an odd number of bytes */
+	odd = in_size & 0x1;
+
+	/* make it even */
+	in_size &= ~1;
+
+	/* First fill up with as much burst frames as possible */
+	while (in_size) {
+
+		cnt = (in_size >= MUX_BURST_SIZE) ? MUX_BURST_SIZE : in_size;
+		*(out_buf + result_size) =
+		    MUX_CONTROL_BYTE(chan_id, MUX_BURST_TRANSFER,
+				     MUX_MASTER_TO_SLAVE);
+		//printk("Burst frame %d bytes\n", cnt);
+		result_size++;
+		*(out_buf + result_size) = cnt / 2;
+		result_size++;
+		memcpy(out_buf + result_size, in_buf, cnt);
+		result_size += cnt;
+		in_size -= cnt;
+		in_buf += cnt;
+	}
+
+	/* Then tackle the odd byte */
+	if (odd) {
+		*(out_buf + result_size) =
+		    MUX_CONTROL_BYTE(chan_id, MUX_DATA_TRANSFER,
+				     MUX_MASTER_TO_SLAVE);
+		result_size++;
+		*(out_buf + result_size) = *in_buf;
+		result_size++;
+
+	}
+
+	return result_size;
+}
+
+/* kfifo put theoretically cannot fail to copy all buffer here */
+void gtm501l_tty_insert_flip_string(struct gtm501l_serial *gtm_ser,
+				    unsigned char *chars,size_t size)
+{
+	int chars_inserted;
+	int copylen;
+	struct tty_struct *tty;
+
+	if (gtm_ser && gtm_ser->tty) {
+		tty= gtm_ser->tty;
+		if (test_bit(TTY_THROTTLED, &tty->flags)) {
+			dprintk(DEBUG_TTY, "received %d bytes while throttled\n", size);
+			copylen=kfifo_in(gtm_ser->throttle_fifo,chars,size);
+			if(copylen!=size)
+				dprintk(DEBUG_TTY, "kfifo_put failed got %d expected %d\n",
+				       copylen, size);
+		}
+		else {
+			chars_inserted=tty_insert_flip_string(tty, chars, size);
+			if(chars_inserted!=size) {
+			
+				size -= chars_inserted;
+				copylen=kfifo_in(gtm_ser->throttle_fifo,
+					  &chars[chars_inserted],
+					  size);
+				if(copylen!=size)
+					dprintk(DEBUG_TTY, "%s kfifo_put failed got %d expected %d\n",
+						copylen, size);
+			}
+			 /* The flip here should force the tty layer
+			  * to throttle if neccessary
+			  */
+			tty_flip_buffer_push(tty);
+		}
+	}
+
+}
+
+#define PORT_SPEC_FLAGS_ENABLED		0x80
+#define PORT_SPEC_FLAGS_TYPE_MASK	0x7f
+#define PORT_SPEC_FLAGS_SERIAL		0x01
+#define PORT_SPEC_FLAGS_NET		0x02
+
+static void gtm501l_decode_version_info(struct gtm501l_device *gtm_dev,
+					unsigned char *buf, int size)
+{
+	int i = 0, chan, flags;
+	u16 version;
+	u16 framelength;
+	struct gtm501l_port_spec *new_port_spec;
+
+	/* Protocol version */
+	memcpy(&version, buf + i, 2);
+	i += 2;
+
+	if(version != 0 || size != 260) {
+		/* Unknown version or size is wrong.. */
+		return;
+	}
+	
+	/* Frame length */
+	memcpy(&framelength, buf + i, 2);
+	i += 2;
+
+	/* Channel descriptors */
+	new_port_spec = kzalloc(sizeof(struct gtm501l_port_spec) * 16, GFP_KERNEL);
+	for(chan = 0; chan < 16; chan++) {
+		flags = buf[i++];
+
+		if(flags | PORT_SPEC_FLAGS_ENABLED)  {
+			new_port_spec[chan].enabled = 1;
+			switch(flags & PORT_SPEC_FLAGS_TYPE_MASK) {
+			case PORT_SPEC_FLAGS_SERIAL:
+				new_port_spec[chan].type = GTM501L_PORT_SPEC_SERIAL;
+				break;
+			case PORT_SPEC_FLAGS_NET:
+				new_port_spec[chan].type = GTM501L_PORT_SPEC_NET;
+				break;
+			default:
+				/* Unknown channel type: disable this channel */
+				new_port_spec[chan].enabled = 0;
+			}
+
+			/* Copy the name */
+			memcpy(&new_port_spec[chan].name, buf + i, 15);
+		}
+		
+		i += 15;
+	}
+
+	/* Activate the new port spec */
+	gtm501l_create_ports(gtm_dev, new_port_spec);
+	kfree(new_port_spec);
+}
+
+
+/**
+ * gtm501l_demux walks through the received SPI message given in in_buf with the length size and copies all
+ * found data into the opened channels given in the gtm_dev structure. The SPI buffer length must be always
+ * a multiple of 2 bytes!
+ * It returns finally the real found useful data inside the SPI frame length
+ */
+static int gtm501l_demux(struct gtm501l_device *gtm_dev, unsigned char *in_buf,
+			 int size)
+{
+	int i = 0, valid = 0, copy;
+	unsigned char temp;
+	struct gtm501l_port_data *port_data;
+	struct gtm501l_serial *gtm_ser = NULL;
+	struct gtm501l_net *gtm_net = NULL;
+	unsigned char old_dcd;
+
+	gtm_dev->empty_transfers++;
+
+	while (i < size) {
+		gtm_ser = NULL;
+		gtm_net = NULL;
+		copy = 0;
+
+		if(spi_b16)
+			swap_buf((u16 *)(in_buf + i), 2);
+
+		/* check for an end of frame sequence */
+		if((in_buf[i]==0) && (in_buf[i+1]==0)) break;
+
+		if(0 && in_buf[i] == 0xFF) { /* TODO: Fill in correct check for version info */
+			copy = in_buf[++i] * 2;
+			i++;
+			if(spi_b16)
+				swap_buf((u16 *)(in_buf + i), copy);
+			gtm501l_decode_version_info(gtm_dev, in_buf + i, copy);
+			i += copy;
+			continue;
+		}
+
+		/* verify valid packet */
+		temp = MUX_DEVICE(in_buf[i]);
+		if (temp != MUX_SLAVE_TO_MASTER) {
+		       /*
+			* ##PH: That should never happen and should counted as errorness data
+			*/
+			i += 2;
+			continue;
+		}
+
+#ifdef DEBUG
+		if(!valid && (i > 0) && (gtm501l_debug & DEBUG_DEMUX)) {
+			int j;
+			dprintk(DEBUG_DEMUX, "First valid byte found at offset %d\n", i);
+			for(j=0 ; j<i ; j++) printk("%.2X ", in_buf[j]);
+			printk("\n");	
+		}
+#endif
+
+		valid = 1;
+
+		//dprintk(DEBUG_DEMUX, "Got valid mux bytes 0x%X 0x%X\n", in_buf[i], in_buf[i+1]);
+
+		/* verify valid channel */
+		temp = MUX_CHANNEL(in_buf[i]);
+		if (temp >= GTM501L_PORT_PER_DEV || !gtm_dev->port_data[temp]) {
+			i += 2;
+			continue;
+		}
+		port_data = gtm_dev->port_data[temp];
+
+		if (port_data->spec.type == GTM501L_PORT_SPEC_NET)
+			gtm_net = &port_data->type.net;
+		else if (port_data->spec.type == GTM501L_PORT_SPEC_SERIAL)
+			gtm_ser = &port_data->type.serial;
+		//dprintk(DEBUG_DEMUX, "For channel %d\n", temp);
+
+		/* start decoding data */
+		temp = MUX_BLOCK_TYPE(in_buf[i]);
+		if (temp == MUX_BURST_TRANSFER) {
+			copy = in_buf[++i] * 2;
+			if( 0 == copy ) copy = MUX_BURST_SIZE;
+			if(spi_b16)
+				swap_buf((u16 *)(in_buf + i + 1), copy);
+		} else if (temp == MUX_DATA_TRANSFER) {
+			copy = 1;
+		}
+
+		if (copy) {
+			gtm_dev->empty_transfers = 0;
+			//dprintk(DEBUG_DEMUX, "\tNeed to copy %d data bytes\n", copy);
+			/* regular data */
+			if( gtm_ser && gtm_ser->tty ) {
+				gtm501l_tty_insert_flip_string(gtm_ser, &in_buf[++i], copy);
+			}
+			else if (gtm_net) {
+/*
+				int j;
+				for(j=i+1;j<(i+1+copy);j++) printk("0x%.2X ", in_buf[j]);
+				printk("\n");
+*/
+				gtm501l_rx_netchar(gtm_net, &in_buf[++i], copy);
+			} else {
+				i++;
+			}
+
+			i += copy;
+			continue;
+		}
+
+		if (temp == MUX_CONTROL_TRANSFER) {
+			//dprintk(DEBUG_DEMUX, "Is a control byte\n");
+			/* control data */
+			temp = in_buf[i + 1];
+			if (MUX_LINK(temp)) {
+				set_bit(GTM501L_TX_FC,
+					&port_data->signal_state);
+				dprintk(DEBUG_DEMUX, "FC set\n");
+			} else {
+				clear_bit(GTM501L_TX_FC,
+					  &port_data->signal_state);
+				dprintk(DEBUG_DEMUX, "FC cleared\n");
+			}
+
+			if (gtm_ser) {
+				old_dcd =
+				    test_bit(GTM501L_DCD,
+					     &port_data->signal_state);
+
+				if (MUX_DCD(temp)) {
+					set_bit(GTM501L_DCD,
+						&port_data->signal_state);
+					dprintk(DEBUG_DEMUX, "DCD set\n");
+				} else {
+					clear_bit(GTM501L_DCD,
+						  &port_data->signal_state);
+					dprintk(DEBUG_DEMUX, "DCD cleared\n");
+				}
+				if (MUX_CTS(temp)) {
+					set_bit(GTM501L_CTS,
+						&port_data->signal_state);
+					dprintk(DEBUG_DEMUX, "CTS set\n");
+				} else {
+					clear_bit(GTM501L_CTS,
+						  &port_data->signal_state);
+					dprintk(DEBUG_DEMUX, "CTS cleared\n");
+				}
+
+				if (MUX_DSR(temp)) {
+					set_bit(GTM501L_DSR,
+						&port_data->signal_state);
+					dprintk(DEBUG_DEMUX, "DSR set\n");
+				} else {
+					clear_bit(GTM501L_DSR,
+						  &port_data->signal_state);
+					dprintk(DEBUG_DEMUX, "DSR cleared\n");
+				}
+
+				if (MUX_RI(temp)) {
+					set_bit(GTM501L_RI,
+						&port_data->signal_state);
+					dprintk(DEBUG_DEMUX, "RI set\n");
+				} else {
+					clear_bit(GTM501L_RI,
+						  &port_data->signal_state);
+					dprintk(DEBUG_DEMUX, "RI cleared\n");
+				}
+
+				if (old_dcd
+				    && !test_bit(GTM501L_DCD,
+						 &port_data->signal_state))
+					if (gtm_ser->tty)
+						tty_hangup(gtm_ser->tty);
+			}
+
+			i += 2;
+		}
+
+	}
+	return i;
+}
+
+static int gtm501l_mux_flow_control(struct gtm501l_port_data *port_data,
+				    unsigned char *out_buf)
+{
+	*out_buf =
+	    MUX_CONTROL_BYTE(port_data->port_id, MUX_CONTROL_TRANSFER,
+			     MUX_MASTER_TO_SLAVE);
+	*(out_buf + 1) =
+	    (test_bit(GTM501L_DTR, &port_data->signal_state)
+	     		? (1 << MUX_DTR_SHIFT) : 0)
+		| (test_bit(GTM501L_RTS, &port_data->signal_state)
+			? (1 << MUX_RTS_SHIFT): 0)
+		| (test_bit(GTM501L_RX_FC, &port_data->signal_state)
+			? (1 << MUX_LINK_SHIFT): 0);
+
+	return 2;
+}
+
+static void gtm501l_timer(unsigned long arg)
+{
+	struct tasklet_struct *tasklet = (struct tasklet_struct *)arg;
+	//printk("Timer fired\n");
+	tasklet_hi_schedule(tasklet);
+}
+
+/* gpio interrupt handler */
+
+static irqreturn_t gtm501l_gpio_interrupt(int irq, void *dev)
+{
+	struct gtm501l_device *gtm_dev = (struct gtm501l_device *)dev;
+	static int count = 0;
+
+	if(!gtm_dev || !test_bit(GTM501L_STATE_PRESENT, &gtm_dev->flags))
+		return IRQ_HANDLED;
+
+	/*
+	 * Using the GPE layer it will set the
+	 * irq to the requested gpio_in line
+	 */
+
+	if(gtm_dev->stats) gtm_dev->stats->wait_finished(gtm_dev->frame_stats);
+
+	if (!(count % 5000))
+	    dprintk(DEBUG_GPIO, "Scheduling\n");
+
+	if(!test_bit(GTM501L_STATE_IO_IN_PROGRESS, &gtm_dev->flags)) {
+
+	    /* If we received no data for the last x
+	     * frames, delay the next transfer */
+	    if(gtm_dev->empty_transfers > GTM501L_MAX_EMPTY && backoff_enabled) {
+		if(!timer_pending(&gtm_dev->timer)) {
+		    gtm_dev->timer.expires = jiffies + GTM501L_BACKOFF_TIMER;
+		    gtm_dev->timer.function = gtm501l_timer;
+		    gtm_dev->timer.data = (unsigned long)&gtm_dev->io_work_tasklet;
+		    add_timer(&gtm_dev->timer);
+		}
+	    }
+	    else
+		tasklet_hi_schedule(&gtm_dev->io_work_tasklet);		
+	}
+	else set_bit(GTM501L_STATE_IO_READY, &gtm_dev->flags);
+
+	count++;
+	
+	return IRQ_HANDLED;
+}
+
+static int gtm501l_prepare_tx_buffer(struct gtm501l_device *gtm_dev)
+{
+	int tx_count = 0;
+	int i, j;
+	unsigned int temp_count;
+	struct gtm501l_port_data *port_data;
+	int round_robin_index;
+	unsigned char *tx_buffer;
+	int len;
+
+	if(gtm_dev->stats) gtm_dev->stats->encode_start_idle_finished(gtm_dev->frame_stats);
+	
+	tx_buffer = gtm_dev->tx_buffer[gtm_dev->tx_buffer_used];
+
+	/* First add flow control events for all ports */
+	for (i = 0; i < GTM501L_PORT_PER_DEV; i++) {
+		port_data = gtm_dev->port_data[i];
+		if(!port_data)
+			continue;
+
+		if (test_and_clear_bit (GTM501L_UPDATE, &port_data->signal_state)) {
+			tx_count += gtm501l_mux_flow_control(port_data,
+				tx_buffer + tx_count);
+		}
+	}
+
+	/* assemble data from buffers from all ports */
+	round_robin_index = gtm_dev->round_robin_index;
+	for (j = round_robin_index; j <
+		(GTM501L_PORT_PER_DEV + round_robin_index) ; j++) {
+		i = j % GTM501L_PORT_PER_DEV;
+		port_data = gtm_dev->port_data[i];
+		if(!port_data)
+			continue;
+
+		/* check if this port is flow controlled */
+		if (test_bit(GTM501L_TX_FC, &port_data->signal_state))
+			continue;
+
+		/* check for data to be sent */
+		temp_count = gtm501l_mux_to_demux(GTM501L_TRANSFER_SIZE - tx_count);
+		temp_count = min(kfifo_len(port_data->tx_fifo), temp_count);
+		if (temp_count) {
+			len = kfifo_out(port_data->tx_fifo, scratch_buf, temp_count);
+#ifdef GTM501L_DEBUG
+			sprintf(debug_buff_name, "gtm501l tx buf port %d %d", i, len);
+			GTM501L_BUFFER_DUMP(debug_buff_name, scratch_buf, temp_count);
+#endif
+			tx_count += gtm501l_mux_data(i, scratch_buf, temp_count,
+				tx_buffer + tx_count);
+			total_spi_write += temp_count;
+			gtm_dev->empty_transfers = 0;
+		}
+		if( port_data->spec.type == GTM501L_PORT_SPEC_NET)
+			gtm501l_push_skb(port_data);
+		else if(port_data->type.serial.tty)
+			tty_wakeup(port_data->type.serial.tty);
+	}
+	gtm_dev->round_robin_index = gtm_dev->round_robin_index + 1;
+	if (gtm_dev->round_robin_index == GTM501L_PORT_PER_DEV)
+		gtm_dev->round_robin_index = 0;
+
+	/* End-Of-Frame marker */
+	temp_count = min(2, GTM501L_TRANSFER_SIZE - tx_count);
+	memset(tx_buffer + tx_count, 0, temp_count);
+	tx_count += temp_count;
+
+	if(spi_b16)
+		swap_buf((u16 *)(tx_buffer), tx_count);
+
+	gtm_dev->tx_count = tx_count;
+	
+	if(gtm_dev->stats) gtm_dev->stats->encode_finished(gtm_dev->frame_stats, tx_count - temp_count);
+
+	return tx_count;
+}
+
+/* serial functions */
+static void gtm501l_io(unsigned long data)
+{
+	struct gtm501l_device *gtm_dev = (struct gtm501l_device *)data;
+	int retval;
+#ifdef GTM501L_DEBUG
+	char debug_buff_name[80];
+#endif
+
+	if (!test_bit(GTM501L_STATE_PRESENT, &gtm_dev->flags))
+		return;
+
+	if (!test_and_set_bit(GTM501L_STATE_IO_IN_PROGRESS, &gtm_dev->flags)) {
+		gtm501l_prepare_tx_buffer(gtm_dev);
+
+		if(gtm_dev->stats) gtm_dev->stats->transfer_start(gtm_dev->frame_stats);
+		
+		spi_message_init(&gtm_dev->spi_msg);
+		gtm_dev->spi_msg.context = gtm_dev;
+		gtm_dev->spi_msg.complete = gtm501l_spi_complete;
+		gtm_dev->spi_msg.is_dma_mapped = 1;
+
+		/* set up our spi transfer */
+		gtm_dev->spi_xfer.len = GTM501L_TRANSFER_SIZE;
+		gtm_dev->spi_xfer.cs_change = 0;
+#if 0
+		gtm_dev->tx_dma[gtm_dev->tx_buffer_used] =
+			dma_map_single(&gtm_dev->spi_dev->dev, gtm_dev->tx_buffer[gtm_dev->tx_buffer_used],
+			GTM501L_TRANSFER_SIZE, DMA_TO_DEVICE);
+		gtm_dev->rx_dma = dma_map_single(&gtm_dev->spi_dev->dev, gtm_dev->rx_buffer,
+			GTM501L_TRANSFER_SIZE, DMA_FROM_DEVICE);
+#else
+		gtm_dev->tx_dma[gtm_dev->tx_buffer_used] = virt_to_phys(gtm_dev->tx_buffer[gtm_dev->tx_buffer_used]);
+		gtm_dev->rx_dma = virt_to_phys(gtm_dev->rx_buffer);
+#endif
+
+		gtm_dev->spi_xfer.tx_dma = gtm_dev->tx_dma[gtm_dev->tx_buffer_used];
+		gtm_dev->spi_xfer.tx_buf = gtm_dev->tx_buffer[gtm_dev->tx_buffer_used];
+		gtm_dev->tx_buffer_used =  (++gtm_dev->tx_buffer_used) % 2;
+		gtm_dev->tx_count = 0;
+		
+		gtm_dev->spi_xfer.rx_dma = gtm_dev->rx_dma;
+		gtm_dev->spi_xfer.rx_buf = gtm_dev->rx_buffer;
+
+		spi_message_add_tail(&gtm_dev->spi_xfer, &gtm_dev->spi_msg);
+
+		retval = spi_async(gtm_dev->spi_dev, &gtm_dev->spi_msg);
+
+		if (retval) {
+			dprintk(DEBUG_SPI, "ERROR: spi_async failed (%d)\n", retval);
+			clear_bit(GTM501L_STATE_IO_IN_PROGRESS,
+				  &gtm_dev->flags);
+			tasklet_hi_schedule(&gtm_dev->io_work_tasklet);
+			return;
+		}
+	} else {
+		dprintk(DEBUG_SPI, "ERROR - gtm501l_io called, but spi still busy\n");
+	}
+
+}
+
+static ssize_t gtm501l_sysfs_channel(struct device *dev, struct device_attribute *attr,
+	char *buf)
+{
+	struct gtm501l_port_data *port_data = NULL;
+	int i;
+
+	/* Look for the port_data matching this device. */
+	if(strcmp("tty", dev->class->name) == 0) {
+		for (i = 0; i < GTM501L_MAX_MINORS; i++) {
+			if (gtm501l_serial_ports[i] &&
+				gtm501l_serial_ports[i]->type.serial.dev == dev) {
+				port_data = gtm501l_serial_ports[i];
+				break;
+			}
+		}
+	}
+	else if(strcmp("net", dev->class->name) == 0) {
+		port_data = net_to_gtm501l_data(to_net_dev(dev));
+	}
+
+	return sprintf(buf, "%s\n", (port_data ? port_data->spec.name : "unknown"));
+}
+
+static DEVICE_ATTR(channel, S_IRUGO, gtm501l_sysfs_channel, NULL);
+
+static void gtm501l_free_port(struct gtm501l_port_data *port_data)
+{
+	/* do device type specific cleanup */
+	if (port_data->spec.type == GTM501L_PORT_SPEC_SERIAL) {
+		/* serial device cleanup */
+		device_remove_file(port_data->type.serial.dev, &dev_attr_channel);
+		gtm501l_serial_ports[port_data->type.serial.minor] = 0;
+		tty_unregister_device(tty_drv, port_data->type.serial.minor);
+		kfifo_free(port_data->type.serial.throttle_fifo);
+	} else if (port_data->spec.type == GTM501L_PORT_SPEC_NET) {
+		/* net device cleanup */
+		device_remove_file(&port_data->type.net.net->dev, &dev_attr_channel);
+		unregister_netdev(port_data->type.net.net);
+		free_netdev(port_data->type.net.net);
+	}
+
+	/* do common device deinitialization */
+	kfifo_free(port_data->tx_fifo);
+	kfree(port_data);
+}
+
+static int gtm501l_get_free_port(void)
+{
+	int i;
+
+	for (i = 0; i < GTM501L_MAX_MINORS; i++) {
+		if (!gtm501l_serial_ports[i])
+			return i;
+	}
+	return -1;
+}
+
+static void gtm501l_create_ports(struct gtm501l_device *gtm_dev,
+				 struct gtm501l_port_spec *specs)
+{
+	struct net_device *net;
+	struct gtm501l_serial *gtm_ser;
+	struct gtm501l_port_data *port_data = NULL;
+	int minor = -1;
+	int i;
+	int status;
+
+	for(i = 0; i < GTM501L_PORT_PER_DEV; i++) {
+		port_data = gtm_dev->port_data[i];
+
+		if(port_data) {
+			if(!specs[i].enabled) {
+				/* A port did exist, but it's gone now */
+				gtm501l_free_port(port_data);
+				gtm_dev->port_data[i] = NULL;
+				continue;
+			}
+			else if (specs[i].type == port_data->spec.type) {
+				/* Old and new port are of the same type,
+				 * only update the name */
+				memcpy(&port_data->spec.name, &specs[i].name, 16);
+				continue;
+			}
+			else {
+				/* Old and new port have different types */
+				gtm501l_free_port(port_data);
+				gtm_dev->port_data[i] = NULL;
+			}
+		}
+
+		/* If this port is not enabled, skip it */
+		if(!specs[i].enabled) {
+			continue;
+		}
+
+		dprintk(DEBUG_INIT, "%d: (%d) %s\n", i, specs[i].type, specs[i].name);
+
+		port_data = kzalloc(sizeof(struct gtm501l_port_data), GFP_KERNEL);
+		if (!port_data)
+			continue;
+
+		memcpy(&port_data->spec, &specs[i], sizeof(struct gtm501l_port_spec));
+
+		spin_lock_init(&port_data->fifo_lock);
+		lockdep_set_class_and_subclass(&port_data->fifo_lock, &gtm501l_key, 0);
+
+		/* common initialization */
+		port_data->spi_itf = gtm_dev;
+		port_data->port_id = i;
+		port_data->tx_fifo =
+		status = kfifo_alloc(port_data->tx_fifo, GTM501L_FIFO_SIZE, GFP_KERNEL);
+		if (status) {
+			printk(KERN_ERR "GTM501 failed kfifo tx alloc %d\n", status);
+			kfree(port_data);
+			continue;
+		}
+		/* device specific initialization */
+		if (port_data->spec.type == GTM501L_PORT_SPEC_SERIAL) {
+			/* serial device */
+			if ((minor = gtm501l_get_free_port()) == -1) {
+				kfree(port_data);
+				continue;
+			}
+			gtm_ser = &port_data->type.serial;
+			gtm_ser->minor = minor;
+			gtm_ser->dev =
+				tty_register_device(tty_drv, minor,
+					&gtm_dev->spi_dev->dev);
+			if (!gtm_ser->dev) {
+				dprintk(DEBUG_INIT, "Registering tty device failed\n");
+				kfree(port_data);
+				continue;
+			}
+			gtm501l_serial_ports[minor] = port_data;
+			spin_lock_init(&gtm_ser->throttle_fifo_lock);
+			lockdep_set_class_and_subclass(&gtm_ser->throttle_fifo_lock, &gtm501l_key, 0);
+			status = kfifo_alloc(gtm_ser->throttle_fifo, GTM501l_THROTTLE_FIFO_SIZE,
+					GFP_KERNEL);
+			if (status) {
+				tty_unregister_device(tty_drv,
+					gtm_ser->minor);
+				kfree(port_data);
+				continue;
+			}
+
+			if (device_create_file(gtm_ser->dev, &dev_attr_channel))
+				dev_err(gtm_ser->dev, "Could not create sysfs file for channel\n");
+		}
+		else if (port_data->spec.type == GTM501L_PORT_SPEC_NET) {
+			/* net device */
+			net = alloc_netdev(sizeof(struct gtm501l_port_data *), "gtm%d",
+					gtm501l_net_init);
+			if (!net) {
+				kfifo_free(port_data->tx_fifo);
+				kfree(port_data);
+				continue;
+			}
+
+			*((struct gtm501l_port_data **)netdev_priv(net)) = port_data;
+			port_data->type.net.net = net;
+
+			if (register_netdev(net)) {
+				free_netdev(net);
+				kfifo_free(port_data->tx_fifo);
+				kfree(port_data);
+				continue;
+			}
+			
+			if (device_create_file(&net->dev, &dev_attr_channel))
+				dev_err(&net->dev, "Could not create sysfs file for channel\n");
+		}
+
+		gtm_dev->port_data[i] = port_data;
+
+	}
+}
+
+static void gtm501l_free_device(struct kref *ref)
+{
+	int i;
+	struct gtm501l_device *gtm_dev =
+	    container_of(ref, struct gtm501l_device, ref);
+	struct gtm501l_port_data *port_data;
+
+	tasklet_kill(&gtm_dev->io_work_tasklet);
+
+	for (i = 0; i < GTM501L_PORT_PER_DEV; i++) {
+		port_data = gtm_dev->port_data[i];
+		if(port_data)
+			gtm501l_free_port(port_data);
+	}
+#ifdef CONFIG_DEBUG_FS
+	gtm501l_debugfs_remove(gtm_dev);
+#endif
+	kfree(gtm_dev);
+}
+
+static void gtm501l_spi_complete(void *ctx)
+{
+	struct gtm501l_device *gtm_dev = (struct gtm501l_device *)ctx;
+	unsigned int rx_count = 0;
+
+	if(gtm_dev->stats) {
+	    gtm_dev->stats->transfer_finished_wait_start(gtm_dev->frame_stats);
+	    gtm_dev->stats->transfer_decode_start(gtm_dev->frame_stats);
+	}
+
+	/* did we get removed meanwhile ? */
+	if (!test_bit(GTM501L_STATE_PRESENT, &gtm_dev->flags))
+		return;
+
+	if (!gtm_dev->spi_msg.status) {
+#if 0
+		dma_unmap_single(&gtm_dev->spi_dev->dev,
+			gtm_dev->tx_dma[(gtm_dev->tx_buffer_used + 1) % 2],
+			GTM501L_TRANSFER_SIZE, DMA_TO_DEVICE);
+		dma_unmap_single(&gtm_dev->spi_dev->dev, gtm_dev->rx_dma,
+			GTM501L_TRANSFER_SIZE, DMA_FROM_DEVICE);
+#endif
+		rx_count = gtm501l_demux(gtm_dev, gtm_dev->rx_buffer,
+			      gtm_dev->spi_msg.actual_length);
+	} else {
+		spi_err_count++;
+		printk("SPI transfer error %d - (%d)\n",
+		       gtm_dev->spi_msg.status, spi_err_count);
+	}
+
+	if(gtm_dev->stats) gtm_dev->stats->decode_finished_may_idle_start(gtm_dev->frame_stats, rx_count);
+	
+	clear_bit(GTM501L_STATE_IO_IN_PROGRESS, &gtm_dev->flags);
+
+	//gtm501l_prepare_tx_buffer(gtm_dev);
+
+	if(test_and_clear_bit(GTM501L_STATE_IO_READY, &gtm_dev->flags))
+			tasklet_hi_schedule(&gtm_dev->io_work_tasklet);		
+}
+
+/* char/tty operations */
+
+static void gtm501l_throttle(struct tty_struct *tty)
+{
+	struct gtm501l_port_data *port_data =
+	    (struct gtm501l_port_data *)tty->driver_data;
+
+	func_enter();	
+
+	if (port_data) {
+		func_exit();
+		return;
+	}
+
+	if(!test_bit(GTM501L_RX_FC, &port_data->signal_state)) {
+		set_bit(GTM501L_RX_FC, &port_data->signal_state);
+		set_bit(GTM501L_UPDATE, &port_data->signal_state);
+	}
+
+	func_exit();
+}
+
+/* To be checked... I can't remember the exact details but the hso driver
+ * needed a hso_unthrottle_tasklet to prevent hso_throttle being
+ * called recursively, I am not sure whether this can happen here.
+ */
+#define UNTHROTTLE_STACK_BUF_SIZE (512)
+static void gtm501l_unthrottle(struct tty_struct *tty)
+{
+	struct gtm501l_port_data *port_data =
+	    (struct gtm501l_port_data *)tty->driver_data;
+	struct gtm501l_serial *gtm_ser;
+	int write_length_remaining, curr_write_len;
+	char   stack_buff[UNTHROTTLE_STACK_BUF_SIZE];
+	struct gtm501l_device *gtm_dev = port_data->spi_itf;
+
+	func_enter();
+
+	if (!port_data) {
+		func_exit();
+		return;
+	}
+
+	gtm_ser=&port_data->type.serial;
+	write_length_remaining=kfifo_len(gtm_ser->throttle_fifo);
+	while (write_length_remaining) {
+		if (test_bit(TTY_THROTTLED, &tty->flags)) {
+			func_exit();
+			return;
+		}
+		curr_write_len = min(write_length_remaining,
+				     UNTHROTTLE_STACK_BUF_SIZE);
+		curr_write_len = kfifo_out(gtm_ser->throttle_fifo,
+					   stack_buff, curr_write_len);
+		curr_write_len =  tty_insert_flip_string
+			(tty, stack_buff,
+			 curr_write_len);
+		write_length_remaining -= curr_write_len;
+		tty_flip_buffer_push(tty);
+	}
+
+	clear_bit(GTM501L_RX_FC, &port_data->signal_state);
+	set_bit(GTM501L_UPDATE, &port_data->signal_state);
+	
+	/* If the timer is currently running, stop it and try to initiate a
+	 * transfer immediately */
+	if(timer_pending(&gtm_dev->timer)) {
+		del_timer_sync(&gtm_dev->timer);
+		gtm501l_io((unsigned long)gtm_dev);
+	}
+		
+	func_exit();
+}
+
+static int gtm501l_tiocmget(struct tty_struct *tty, struct file *filp)
+{
+	unsigned int value;
+	struct gtm501l_port_data *port_data =
+	    (struct gtm501l_port_data *)tty->driver_data;
+
+	func_enter();
+
+	if (!port_data) {
+		func_exit();
+		return 0;
+	}
+
+	value =
+	    (test_bit(GTM501L_RTS, &port_data->signal_state) ? TIOCM_RTS : 0) |
+	    (test_bit(GTM501L_DTR, &port_data->signal_state) ? TIOCM_DTR : 0) |
+	    (test_bit(GTM501L_CTS, &port_data->signal_state) ? TIOCM_CTS : 0) |
+	    (test_bit(GTM501L_DSR, &port_data->signal_state) ? TIOCM_DSR : 0) |
+	    (test_bit(GTM501L_DCD, &port_data->signal_state) ? TIOCM_CAR : 0) |
+	    (test_bit(GTM501L_RI, &port_data->signal_state) ? TIOCM_RNG : 0);
+
+	func_exit();
+	return value;
+}
+
+static int gtm501l_tiocmset(struct tty_struct *tty, struct file *filp,
+			    unsigned int set, unsigned int clear)
+{
+	struct gtm501l_port_data *port_data =
+	    (struct gtm501l_port_data *)tty->driver_data;
+
+	func_enter();
+
+	if (!port_data) {
+		func_exit();
+		return -ENODEV;
+	}
+
+	if (set & TIOCM_RTS)
+		set_bit(GTM501L_RTS, &port_data->signal_state);
+	if (set & TIOCM_DTR)
+		set_bit(GTM501L_DTR, &port_data->signal_state);
+
+	if (clear & TIOCM_RTS)
+		clear_bit(GTM501L_RTS, &port_data->signal_state);
+	if (clear & TIOCM_DTR)
+		clear_bit(GTM501L_DTR, &port_data->signal_state);
+
+	set_bit(GTM501L_UPDATE, &port_data->signal_state);
+
+	func_exit();
+	return 0;
+}
+
+static int gtm501l_open(struct tty_struct *tty, struct file *filp)
+{
+	struct gtm501l_serial *gtm_ser = NULL;
+	struct gtm501l_port_data *port_data;
+
+	func_enter();
+
+	if ((tty->index > GTM501L_MAX_MINORS)
+	    || (!gtm501l_serial_ports[tty->index])) {
+		func_exit();
+		return -ENODEV;
+	}
+
+	port_data = gtm501l_serial_ports[tty->index];
+	gtm_ser = &port_data->type.serial;
+
+	if (!test_bit(GTM501L_STATE_PRESENT, &port_data->spi_itf->flags)) {
+		func_exit();
+		return -ENODEV;
+	}
+
+	gtm_ser->open++;
+	tty->driver_data = port_data;
+	tty->low_latency = 1;
+	gtm_ser->tty = tty;
+	_gtm501l_set_termios(tty, NULL);
+
+	/* signal_update_needed flag will be set by tiocmset */
+	clear_bit(GTM501L_RX_FC, &port_data->signal_state);
+	gtm501l_tiocmset(tty, filp, TIOCM_DTR | TIOCM_RTS, 0);
+
+	kref_get(&port_data->spi_itf->ref);
+	func_exit();
+	return 0;
+}
+
+static void gtm501l_close(struct tty_struct *tty, struct file *filp)
+{
+	struct gtm501l_serial *gtm_ser = NULL;
+	struct gtm501l_port_data *port_data =
+	    (struct gtm501l_port_data *)tty->driver_data;
+
+	func_enter();
+
+	if ((tty->index > GTM501L_MAX_MINORS) || !port_data) {
+		func_exit();
+		return;
+	}
+
+	gtm_ser = &port_data->type.serial;
+
+	/*
+ 	* ugh, the refcounting... unfortunately open() & close()'s aren't always executed symmetrically.
+ 	* There are cases where after a failed open you can still get a close(). We can't handle those
+ 	* here. File a tty layer bug.
+ 	*/
+	if(--gtm_ser->open >= 0) {
+		kref_put(&port_data->spi_itf->ref, gtm501l_free_device);
+		if( gtm_ser->open == 0) {
+			kfifo_reset(port_data->tx_fifo);
+			/* signal_update_needed flag will be set by tiocmset */
+			set_bit(GTM501L_RX_FC, &port_data->signal_state);
+			gtm501l_tiocmset(tty, filp, 0, TIOCM_DTR | TIOCM_RTS);
+			gtm_ser->tty = NULL;
+		}
+	} else gtm_ser->open = 0;
+	
+	func_exit();
+}
+
+static int gtm501l_write(struct tty_struct *tty, const unsigned char *buf,
+			 int count)
+{
+	struct gtm501l_port_data *port_data =
+	    (struct gtm501l_port_data *)tty->driver_data;
+	struct gtm501l_serial *gtm_ser;
+	unsigned int tx_count;
+	unsigned char *tmp_buf = (unsigned char *)buf;
+	struct gtm501l_device *gtm_dev = port_data->spi_itf;
+
+	func_enter();
+
+	if (!port_data) {
+		func_exit();
+		return -ENODEV;
+	}
+
+	gtm_ser = &port_data->type.serial;
+
+	tx_count = kfifo_in(port_data->tx_fifo, tmp_buf, count);
+	total_tty_write+=tx_count;
+
+	/* If the timer is currently running, stop it and try to initiate a
+	 * transfer immediately */
+	if(timer_pending(&gtm_dev->timer)) {
+		del_timer_sync(&gtm_dev->timer);
+		gtm501l_io((unsigned long)gtm_dev);
+	}
+
+	//printk("Write: wrote %d bytes in fifo (total = %d)\n", tx_count, total_tty_write);
+	
+	func_exit();
+
+	return tx_count;
+}
+
+static int gtm501l_write_room(struct tty_struct *tty)
+{
+	struct gtm501l_port_data *port_data =
+	    (struct gtm501l_port_data *)tty->driver_data;
+
+	if (!port_data) {
+		return -ENODEV;
+	}
+
+	//func_enter();
+
+	return GTM501L_FIFO_SIZE - kfifo_len(port_data->tx_fifo);
+}
+
+static void _gtm501l_set_termios(struct tty_struct *tty, struct ktermios *old)
+{
+	struct gtm501l_port_data *port_data =
+	    (struct gtm501l_port_data *)tty->driver_data;
+	struct gtm501l_serial *serial;
+	struct ktermios *termios;
+
+	if ((!tty) || (!tty->termios) || (!port_data)) {
+		printk(KERN_ERR "%s: no tty structures", __func__);
+		return;
+	}
+
+	serial = &port_data->type.serial;
+	/*
+	 *          * The default requirements for this device are:
+	 *                   */
+	termios = tty->termios;
+	termios->c_iflag &= ~(IGNBRK	/* disable ignore break */
+			      | BRKINT	/* disable break causes interrupt */
+			      | PARMRK	/* disable mark parity errors */
+			      | ISTRIP	/* disable clear high bit of input characters */
+			      | INLCR	/* disable translate NL to CR */
+			      | IGNCR	/* disable ignore CR */
+			      | ICRNL	/* disable translate CR to NL */
+			      | IXON);	/* disable enable XON/XOFF flow control */
+
+	/* disable postprocess output characters */
+	termios->c_oflag &= ~OPOST;
+
+	termios->c_lflag &= ~(ECHO	/* disable echo input characters */
+			      | ECHONL	/* disable echo new line */
+			      | ICANON	/* disable erase, kill, werase, and rprnt
+					   special characters */
+			      | ISIG	/* disable interrupt, quit, and suspend special
+					   characters */
+			      | IEXTEN);	/* disable non-POSIX special characters */
+
+	termios->c_cflag &= ~(CSIZE	/* no size */
+			      | PARENB	/* disable parity bit */
+			      | CBAUD	/* clear current baud rate */
+			      | CBAUDEX);	/* clear current buad rate */
+	termios->c_cflag |= CS8;	/* character size 8 bits */
+
+	tty_encode_baud_rate(serial->tty, 115200, 115200);
+	/*
+	 * Force low_latency on; otherwise the pushes are scheduled;
+	 * this is bad as it opens up the possibility of dropping bytes
+	 * on the floor.  We don't want to drop bytes on the floor. :)
+	 */
+	serial->tty->low_latency = 1;
+	serial->tty->termios->c_cflag |= B115200;	/* baud rate 115200 */
+	return;
+}
+
+static void gtm501l_set_termios(struct tty_struct *tty, struct ktermios *old)
+{
+	struct gtm501l_port_data *port_data =
+	    (struct gtm501l_port_data *)tty->driver_data;
+	struct gtm501l_serial *serial;
+
+	func_enter();
+
+	if (!port_data) {
+		func_exit();
+		return;
+	}
+	serial = &port_data->type.serial;
+
+	/* the actual setup */
+	if (serial->tty)
+		_gtm501l_set_termios(tty, old);
+	else
+		tty->termios = old;
+
+	/* done */
+	func_exit();
+	return;
+}
+
+static int gtm501l_chars_in_buffer(struct tty_struct *tty)
+{
+	struct gtm501l_port_data *port_data =
+	    (struct gtm501l_port_data *)tty->driver_data;
+
+	if (!port_data)
+		return -ENODEV;
+
+	//func_enter();
+
+	return kfifo_len(port_data->tx_fifo);
+}
+
+static struct mrst_spi_chip mrst_gtm501l = {
+	.poll_mode = 0,
+	.enable_dma = 1,
+	.type = SPI_FRF_SPI,
+};
+
+/* spi operations */
+
+static int gtm501l_spi_probe(struct spi_device *spi)
+{
+	struct gtm501l_device *gtm_dev;
+	int i;
+
+	func_enter();
+
+	/* we check here only the SPI mode and correct them, if needed */
+	if (GTM501L_SPI_MODE != (spi->mode & (SPI_CPHA | SPI_CPOL | SPI_CS_HIGH | SPI_LSB_FIRST | SPI_3WIRE))) {
+	        pr_warning("%s: SPI mode wrong setup, found %d, correct to %d\n",
+			   DRVNAME, spi->mode, GTM501L_SPI_MODE);
+		spi->mode = GTM501L_SPI_MODE | (SPI_LOOP & spi->mode);
+	}
+
+	if (spi->mode & SPI_LOOP) {
+	        pr_warning("%s: SPI device in loop back\n", DRVNAME);
+	}
+
+	/* The Bit_per_word and the maximum speed has to be setup by us, the protocol driver */
+	if(spi_b16)
+		spi->bits_per_word = 16;
+	else
+		spi->bits_per_word = 8;
+
+	spi->max_speed_hz = GTM501L_SPI_SPEED;
+
+	spi->controller_data = &mrst_gtm501l;
+
+	if (spi_setup(spi)) {
+		pr_err("%s: SPI setup does wasn't successful\n", DRVNAME);
+		func_exit();
+		return -ENODEV;
+	}
+
+	/* initialize structure to hold our device variables */
+	gtm_dev = kzalloc(sizeof(struct gtm501l_device), GFP_ATOMIC);
+
+	if (!gtm_dev) {
+		func_exit();
+		return -ENOMEM;
+	}
+	gtm_dev->spi_dev = spi;
+	kref_init(&gtm_dev->ref);
+
+	/*initialize transfer and dma buffers */
+	for(i = 0; i < 2; i++) {
+		gtm_dev->tx_buffer[i] = kzalloc(GTM501L_TRANSFER_SIZE, GFP_KERNEL | GFP_DMA);
+		if( 0 == gtm_dev->tx_buffer[i]) {
+			pr_err("%s: DMA-TX[%d] buffer allocation failed\n", DRVNAME, i);
+			func_exit();
+			return -EIO;
+		}
+	}
+	gtm_dev->rx_buffer = kzalloc(GTM501L_TRANSFER_SIZE, GFP_KERNEL | GFP_DMA);
+	if( 0 == gtm_dev->rx_buffer) {
+		pr_err("%s: DMA-RX buffer allocation failed\n", DRVNAME);
+		func_exit();
+		return -EIO;
+	}
+
+	/* create our tty/net ports */
+	gtm501l_create_ports(gtm_dev, gtm501l_default_port_spec);
+
+	spi_set_drvdata(spi, gtm_dev);
+
+	tasklet_init(&gtm_dev->io_work_tasklet,
+		     (void (*)(unsigned long))gtm501l_io,
+		     (unsigned long)gtm_dev);
+
+	init_timer(&gtm_dev->timer);
+
+#ifdef CONFIG_DEBUG_FS
+	gtm501l_debugfs_init(gtm_dev);
+#endif
+
+	/*
+ 	 * Init GPIO and IRQ, if at least the gpio parameter is set
+	 */
+	if (gpio_in < 0)
+		gpio_in = GTM501L_GPIO0;
+
+	if (request_irq(spi->irq, gtm501l_gpio_interrupt, GTM501L_IRQ_TYPE,
+			"option", (void *)gtm_dev)) {
+		kref_put(&gtm_dev->ref, gtm501l_free_device);
+		func_exit();
+		return -EIO;
+	}
+
+	set_bit(GTM501L_STATE_PRESENT, &gtm_dev->flags);
+	/*
+	 * Schedule tasklet once in case the gpio is active at probe time.
+	 * Otherwise wait for the next interrupt
+	 */
+	gtm501l_gpio_interrupt(spi->irq, (void *)gtm_dev);
+
+	func_exit();
+	return 0;
+}
+
+static int gtm501l_spi_remove(struct spi_device *spi)
+{
+	struct gtm501l_device *gtm_dev =
+	    (struct gtm501l_device *)spi_get_drvdata(spi);
+
+	func_enter();
+
+	del_timer_sync(&gtm_dev->timer);
+
+	clear_bit(GTM501L_STATE_PRESENT, &gtm_dev->flags);
+	free_irq(spi->irq, gtm_dev);
+	kfree(gtm_dev->tx_buffer[0]);
+	kfree(gtm_dev->tx_buffer[1]);
+	kfree(gtm_dev->rx_buffer);
+	spi_set_drvdata(spi, NULL);
+	kref_put(&gtm_dev->ref, gtm501l_free_device);
+
+	func_exit();
+	return 0;
+}
+
+static void gtm501l_spi_shutdown(struct spi_device *spi)
+{
+	func_enter();
+}
+
+static int gtm501l_spi_suspend(struct spi_device *spi, pm_message_t msg)
+{
+	func_enter();
+	return 0;
+}
+
+static int gtm501l_spi_resume(struct spi_device *spi)
+{
+	func_enter();
+	return 0;
+}
+
+static struct tty_operations gtm501l_serial_ops = {
+	.open = gtm501l_open,
+	.close = gtm501l_close,
+	.write = gtm501l_write,
+	.write_room = gtm501l_write_room,
+	.set_termios = gtm501l_set_termios,
+	.chars_in_buffer = gtm501l_chars_in_buffer,
+	.tiocmget = gtm501l_tiocmget,
+	.tiocmset = gtm501l_tiocmset,
+	.throttle = gtm501l_throttle,
+	.unthrottle = gtm501l_unthrottle
+};
+
+static struct spi_driver gtm501l_spi_driver = {
+	.driver = {
+		   .name = "spi_opt_modem",
+		   .bus = &spi_bus_type,
+		   .owner = THIS_MODULE
+	},
+	.probe = gtm501l_spi_probe,
+	.remove = __devexit_p(gtm501l_spi_remove),
+	.shutdown = gtm501l_spi_shutdown,
+	.suspend = gtm501l_spi_suspend,
+	.resume = gtm501l_spi_resume,
+};
+
+/* module exit point */
+static void __exit gtm501l_exit(void)
+{
+	func_enter();
+	tty_unregister_driver(tty_drv);
+	spi_unregister_driver(&gtm501l_spi_driver);
+	dprintk(DEBUG_CLEANUP, "GTM501L driver removed\n");
+	func_exit();
+}
+
+/* module entry point */
+static int __init gtm501l_init(void)
+{
+	int result = 0;
+
+	func_enter();
+
+/*	gtm501l_pmic_init_voltages();*/
+	gtm501l_pmic_set_wwandisablen(1);
+	
+	gtm501l_pmic_set_wwanresetn(0);
+	msleep(100);
+	gtm501l_pmic_set_wwanresetn(1);
+
+	memset(gtm501l_serial_ports, 0, sizeof(gtm501l_serial_ports));
+	memset(gtm501l_termios, 0, sizeof(gtm501l_termios));
+	memset(gtm501l_termios_locked, 0, sizeof(gtm501l_termios_locked));
+
+	/* initialize lower-edge tty driver */
+	tty_drv = alloc_tty_driver(GTM501L_MAX_MINORS);
+	if (!tty_drv) {
+		func_exit();
+		return -ENOMEM;
+	}
+
+	tty_drv->magic = TTY_DRIVER_MAGIC;
+	tty_drv->owner = THIS_MODULE;
+	tty_drv->driver_name = "gtm501l";
+	tty_drv->name = "ttyGTM";
+	tty_drv->minor_start = 0;
+	tty_drv->num = GTM501L_MAX_MINORS;
+	tty_drv->type = TTY_DRIVER_TYPE_SERIAL;
+	tty_drv->subtype = SERIAL_TYPE_NORMAL;
+	tty_drv->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
+	tty_drv->init_termios = tty_std_termios;
+	tty_drv->init_termios.c_cflag = B115200 | CS8 | CREAD | HUPCL | CLOCAL;
+	tty_drv->termios = gtm501l_termios;
+	tty_drv->termios_locked = gtm501l_termios_locked;
+
+	tty_set_operations(tty_drv, &gtm501l_serial_ops);
+
+	result = tty_register_driver(tty_drv);
+	if (result) {
+		printk(KERN_ERR "%s - tty_register_driver failed(%d)\n",
+		       __func__, result);
+		func_exit();
+		return result;
+	}
+
+	/*
+	   initialize upper-edge spi driver. needs to be done after tty initialization because the spi probe will
+	   race
+	 */
+	result = spi_register_driver(&gtm501l_spi_driver);
+	if (result) {
+		printk(KERN_ERR "%s - spi_register_driver failed(%d)\n",
+		       __func__, result);
+		tty_unregister_driver(tty_drv);
+		func_exit();
+		return result;
+	}
+
+	dprintk(DEBUG_INIT, "GTM501L driver initialized successfully\n");
+	func_exit();
+	return 0;
+}
+
+static int gtm501l_net_open(struct net_device *net)
+{
+	struct gtm501l_port_data *port_data = net_to_gtm501l_data(net);
+
+	func_enter();
+
+	port_data->type.net.rx_state = WAIT_IP;
+	port_data->type.net.sync_lost = 0;
+	port_data->type.net.rx_buf_size = 0;
+	port_data->type.net.rx_buf_missing = sizeof(struct iphdr);
+
+	/* update remote side it's ok to send us data */
+	clear_bit(GTM501L_RX_FC, &port_data->signal_state);
+	set_bit(GTM501L_UPDATE, &port_data->signal_state);
+	netif_start_queue(net);
+	func_exit();
+	return 0;
+}
+
+static int gtm501l_net_close(struct net_device *net)
+{	
+	struct gtm501l_port_data *port_data = net_to_gtm501l_data(net);
+
+	func_enter();
+
+	/* stop remote side from sending us data */
+	set_bit(GTM501L_RX_FC, &port_data->signal_state);
+	set_bit(GTM501L_UPDATE, &port_data->signal_state);
+	netif_stop_queue(net);
+	func_exit();
+	return 0;
+}
+
+static void gtm501l_push_skb(struct gtm501l_port_data *port_data)
+{
+	struct gtm501l_net *gtm_net = &port_data->type.net;
+	struct sk_buff *skb = gtm_net->tx_skb;
+	unsigned int len;
+
+	func_enter();
+
+	if (skb && gtm_net->net->flags & IFF_UP) {
+		len = kfifo_in(port_data->tx_fifo, skb->data, skb->len);
+		skb_pull(skb, len);
+		if (skb->len == 0) {
+			// dev_kfree_skb(skb); // TODO: This causes a crash...
+			gtm_net->tx_skb = NULL;
+			netif_start_queue(gtm_net->net);
+		}
+	}
+	
+	func_exit();
+}
+
+static int gtm501l_net_start_xmit(struct sk_buff *skb, struct net_device *net)
+{
+	int result = 0;
+	struct gtm501l_port_data *port_data = net_to_gtm501l_data(net);
+	struct gtm501l_net *gtm_net = &port_data->type.net;
+
+	func_enter();
+
+	netif_stop_queue(net);
+
+	if (gtm_net->tx_skb) {
+		printk(KERN_ERR "%s tx_skb not null\n", __func__);
+		result = -EIO;
+	} else {
+		gtm_net->tx_skb = skb;
+		gtm501l_push_skb(port_data);
+	}
+	if (result) {
+		STATS(net).tx_errors++;
+		netif_start_queue(net);
+	} else {
+		STATS(net).tx_packets++;
+		STATS(net).tx_bytes += skb->len;
+		/* And tell the kernel when the last transmit started. */
+		net->trans_start = jiffies;
+	}
+	/* we're done */
+	func_exit();
+	return result;
+}
+
+#ifndef NETDEVICE_HAS_STATS
+static struct net_device_stats *gtm501l_net_get_stats(struct net_device *net)
+{
+	return &STATS(net);
+}
+#endif
+
+/* called when a packet did not ack after watchdogtimeout */
+static void gtm501l_net_tx_timeout(struct net_device *net)
+{
+	func_enter();
+
+	/* Tell syslog we are hosed. */
+	dev_warn(&net->dev, "Tx timed out.\n");
+
+	/* Update statistics */
+	STATS(net).tx_errors++;
+
+	func_exit();
+}
+
+static const struct net_device_ops gtm501l_netdev_ops = {
+	.ndo_open	= gtm501l_net_open,
+	.ndo_stop	= gtm501l_net_close,
+	.ndo_start_xmit	= gtm501l_net_start_xmit,
+#ifndef NETDEVICE_HAS_STATS
+	.ndo_get_stats	= gtm501l_net_get_stats,
+#endif
+	.ndo_tx_timeout	= gtm501l_net_tx_timeout,
+};
+
+static void gtm501l_net_init(struct net_device *net)
+{
+	func_enter();
+
+	/* fill in the other fields */
+	net->netdev_ops = &gtm501l_netdev_ops;
+	net->watchdog_timeo = GTM501L_NET_TX_TIMEOUT;
+	net->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST;
+	net->type = ARPHRD_NONE;
+	net->mtu = GTM501L_DEFAULT_MTU;
+	net->tx_queue_len = 10;
+
+	func_exit();
+}
+
+struct gtm501l_device *gtm501l_set_stats_ops(struct gtm501_stats_ops *stats)
+{
+	struct gtm501l_device *gtm_dev = NULL;
+	int i;
+
+	/* Look for gtm_dev */
+	for (i = 0; i < GTM501L_MAX_MINORS; i++) {
+		if (gtm501l_serial_ports[i] &&
+			gtm501l_serial_ports[i]->spi_itf) {
+			gtm_dev = gtm501l_serial_ports[i]->spi_itf;
+			break;
+		}
+	}
+
+	if(gtm_dev)
+		gtm_dev->stats = stats;
+	
+	return gtm_dev;
+}
+
+/* module definitions */
+module_init(gtm501l_init);
+module_exit(gtm501l_exit);
+
+module_param_named(backoff, backoff_enabled, uint, S_IRUGO);
+MODULE_PARM_DESC(backoff, "Enable (1) or disable (0) backoff timer.");
+
+module_param_named(gpi, gpio_in, uint, S_IRUGO);
+MODULE_PARM_DESC(gpi, "GPIO input base address. (default: -1 => automatic)");
+
+module_param_named(b16, spi_b16, bool, S_IRUGO);
+MODULE_PARM_DESC(b16, "SPI 16Bit/word or 8Bit/word, default 16Bit");
+
+#ifdef DEBUG
+module_param_named(debug, gtm501l_debug, uint, S_IRUGO);
+MODULE_PARM_DESC(debug, "Debug flags");
+#endif
+
+MODULE_AUTHOR("Option Wireless");
+MODULE_DESCRIPTION("GTM501L spi driver");
+MODULE_LICENSE("GPL");
+MODULE_INFO(Version, "0.5pre1-option");
+
+EXPORT_SYMBOL_GPL(gtm501l_debug);
+EXPORT_SYMBOL_GPL(gtm501l_debug_printk);
+EXPORT_SYMBOL_GPL(gtm501l_set_stats_ops);
+
Index: linux-2.6.33/drivers/spi/gtm501l_spi.h
===================================================================
--- /dev/null
+++ linux-2.6.33/drivers/spi/gtm501l_spi.h
@@ -0,0 +1,329 @@
+/****************************************************************************
+ *
+ * Driver for the Option GTM501L spi modem.
+ *
+ * Copyright (C) 2008 Option International
+ * Copyright (C) 2008 Filip Aben <f.aben@option.com>
+ *                    Denis Joseph Barrow <d.barow@option.com>
+ *                    Jan Dumon <j.dumon@option.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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
+ * USA
+ *
+ *
+ *
+ *****************************************************************************/
+
+#ifndef _GTM501L_SPI_H
+#define _GTM501L_SPI_H
+#include <linux/version.h>
+#include <linux/tty.h>
+#include <linux/device.h>
+#include <linux/spi/spi.h>
+
+#include <linux/netdevice.h>
+#include <linux/ip.h>
+
+#define DRVNAME     "gtm501l"
+
+#define DEBUG
+
+#ifdef DEBUG
+#define DEBUG_FLOW	(1 << 0)
+#define DEBUG_INIT	(1 << 1)
+#define DEBUG_CLEANUP	(1 << 2)
+#define DEBUG_TTY	(1 << 3)
+#define DEBUG_NET	(1 << 4)
+#define DEBUG_MUX	(1 << 5)
+#define DEBUG_DEMUX	(1 << 6)
+#define DEBUG_SPI	(1 << 7)
+#define DEBUG_GPIO	(1 << 8)
+
+#define dprintk(f, str...) if(gtm501l_debug & f) gtm501l_debug_printk(__func__, __LINE__, str)
+
+#define GTM501L_BUFFER_DUMP(prefix_str,buf,len)				\
+	print_hex_dump(KERN_DEBUG,prefix_str, DUMP_PREFIX_OFFSET,16,1,buf,len,1)
+
+void gtm501l_debug_printk(const char *function, int line, char *format, ...);
+extern int gtm501l_debug;
+
+#else
+#define dprintk(f, str...)
+#define GTM501L_BUFFER_DUMP(prefix_str,buf,len)
+#endif
+
+#define func_enter()  dprintk(DEBUG_FLOW, "enter\n")
+#define func_exit() dprintk(DEBUG_FLOW, "exit\n")
+
+#define GTM501L_DEFAULT_MTU			1500
+#define GTM501L_DEFAULT_MRU			2500
+#define	GTM501L_NET_TX_TIMEOUT			(HZ * 10)
+
+#define GTM501L_IRQ_TYPE			IRQ_TYPE_EDGE_FALLING
+#define GTM501L_GPIO_TARGET			0
+#define GTM501L_GPIO0				0x3c		/* default use Langwell GPIO60 */
+
+/* various macro definitions */
+#define GTM501L_MAX_MINORS			256
+#define GTM501L_PORT_PER_DEV			16
+#define GTM501L_TRANSFER_SIZE			2040
+/* GTM501l_THROTTLE_FIFO_SIZE must be a power of 2
+ * & larger than GTM501L_TRANSFER_SIZE */
+#define GTM501l_THROTTLE_FIFO_SIZE		4096
+#define GTM501L_FIFO_SIZE			4096
+
+/* device flags bitfield definitions */
+#define GTM501L_STATE_PRESENT 			0
+#define GTM501L_STATE_IO_IN_PROGRESS		1
+#define GTM501L_STATE_IO_READY			2
+
+#define MUX_CHANNEL(x)		((x >> MUX_CHANNEL_SHIFT) & 0xF)
+#define MUX_CHANNEL_SHIFT	0
+#define MUX_BLOCK_TYPE(x)	((x >> MUX_BLOCK_TYPE_SHIFT) & 0x3)
+#define MUX_BLOCK_TYPE_SHIFT	4
+#define MUX_DEVICE(x)		((x >> MUX_DEVICE_SHIFT) & 0x3)
+#define MUX_DEVICE_SHIFT	6
+#define MUX_BURST_SIZE		512
+
+#define MUX_DATA_TRANSFER	0
+#define MUX_BURST_TRANSFER	1
+#define MUX_CONTROL_TRANSFER	2
+
+#define MUX_CONTROL_BYTE(channel,type,device)	(	\
+		(channel<<MUX_CHANNEL_SHIFT) |		\
+		(type<<MUX_BLOCK_TYPE_SHIFT) |		\
+		(device<<MUX_DEVICE_SHIFT)		\
+		)
+
+#define MUX_DCD(x)			((x >> MUX_DCD_SHIFT) & 0x1)
+#define MUX_DCD_SHIFT		0
+#define MUX_CTS(x)			((x >> MUX_CTS_SHIFT) & 0x1)
+#define MUX_CTS_SHIFT		1
+#define MUX_DSR(x)			((x >> MUX_DSR_SHIFT) & 0x1)
+#define MUX_DSR_SHIFT		2
+#define MUX_RI(x)			((x >> MUX_RI_SHIFT) & 0x1)
+#define MUX_RI_SHIFT		3
+#define MUX_DTR(x)			((x >> MUX_DTR_SHIFT) & 0x1)
+#define MUX_DTR_SHIFT		4
+#define MUX_RTS(x)			((x >> MUX_RTS_SHIFT) & 0x1)
+#define MUX_RTS_SHIFT		5
+#define MUX_LINK(x)			((x >> MUX_LINK_SHIFT) & 0x1)
+#define MUX_LINK_SHIFT		7
+
+#define MUX_INVALID			0
+#define MUX_SLAVE_TO_MASTER		1
+#define MUX_MASTER_TO_SLAVE		2
+#define MUX_INVALID2			3
+
+#define GTM501L_SPI_MODE                SPI_MODE_1       /* SPI Mode 1 currently used */
+
+#define GTM501L_SPI_SPEED               12500000
+
+/* flow control bitfields */
+#define GTM501L_DCD				0
+#define GTM501L_CTS				1
+#define GTM501L_DSR				2
+#define GTM501L_RI				3
+#define GTM501L_DTR				4
+#define GTM501L_RTS				5
+#define GTM501L_TX_FC				6
+#define GTM501L_RX_FC				7
+#define GTM501L_UPDATE				8
+
+#define GTM501L_MAX_EMPTY			500
+#define GTM501L_BACKOFF_TIMER			(HZ / 2)
+
+struct gtm501l_device {
+	struct spi_device *spi_dev;
+	struct kref ref;
+	struct gtm501l_port_data *port_data[GTM501L_PORT_PER_DEV];
+	struct tasklet_struct io_work_tasklet;
+	unsigned long flags;
+	dma_addr_t rx_dma;
+	dma_addr_t tx_dma[2];
+
+	unsigned char *rx_buffer;
+	unsigned char *tx_buffer[2];
+	int tx_buffer_used;
+	int tx_count;
+	
+	struct spi_message spi_msg;
+	struct spi_transfer spi_xfer;
+
+	int gpio_irq;
+	int round_robin_index;
+
+	struct timer_list timer;
+	int empty_transfers;
+
+#ifdef CONFIG_DEBUG_FS
+	struct dentry *debugfs;              /* debugfs parent directory */
+	struct gtm501l_frame_stats *frame_stats;
+#endif
+	
+	struct gtm501_stats_ops *stats;
+};
+
+struct gtm501l_serial {
+	struct device *dev;
+	struct tty_struct *tty;
+	struct kfifo *throttle_fifo;
+	spinlock_t    throttle_fifo_lock;
+	int minor;
+	int open;
+};
+
+enum rx_parse_state {
+	syncing,
+	getting_frame_len,
+	filling_skb,
+	WAIT_IP,
+	WAIT_DATA,
+	WAIT_SYNC
+};
+
+#undef NETDEVICE_HAS_STATS
+
+struct gtm501l_net {
+	enum rx_parse_state rx_state;
+	int sync_lost;
+	struct sk_buff *tx_skb;
+	struct sk_buff *rx_skb;
+	unsigned short rx_frame_len;
+	struct net_device *net;
+	unsigned short rx_buf_size;
+	unsigned short rx_buf_missing;
+	struct iphdr rx_ip_hdr;
+#ifndef NETDEVICE_HAS_STATS
+	struct net_device_stats	stats;
+#endif
+};
+
+#define GTM501L_PORT_SPEC_SERIAL	0
+#define GTM501L_PORT_SPEC_NET		1
+
+struct gtm501l_port_spec {
+	int enabled;
+	int type;
+	char name[16];
+};
+
+struct gtm501l_port_data {
+	struct gtm501l_device *spi_itf;
+	int port_id;
+	struct gtm501l_port_spec spec;
+	struct kfifo *tx_fifo;
+	spinlock_t fifo_lock;
+	unsigned long signal_state;
+	union {
+		struct gtm501l_serial serial;
+		struct gtm501l_net net;
+	} type;
+};
+
+#define net_to_gtm501l_data(net) *((struct gtm501l_port_data **)netdev_priv(net))
+
+#ifdef NETDEVICE_HAS_STATS
+#define STATS(net) ((net)->stats)
+#else
+#define STATS(net) (((struct gtm501l_port_data *)net_to_gtm501l_data(net))->type.net.stats)
+#endif
+
+#ifdef CONFIG_DEBUG_FS
+
+/**
+ * transfer SPI frame sequence, can be used for global sequence state or for per CPU seq state variable
+ */
+enum gtm501l_fsequence {			/* frame sequence start states */
+	none,					/* undefined state */
+	idle,					/* idle state host driver waits */
+	encode,					/* encoding SPI frame */
+	encode_interrupt_decode,		/* encoding SPI frame started and interrupts decoding frame */
+	decode,					/* decoding SPI frame */
+	decode_interrupt_encode			/* decoding SPI frame started and interrupts decoding frame */
+};
+
+/**
+ * job time with the support for interrupt time correction, which me be used only for encoding and decoding time
+ * measurements
+ */
+struct gtm501l_jtime {                          /* job time */
+        ktime_t start;				/* start time for that job */
+        ktime_t correct;			/* correction time, if job was interrupted */
+        u32 dt;                                 /* delta time need for that job in us */
+        u32 min_dt;                             /* min time need for that job is us */
+        u32 max_dt;                             /* max time need for that job is us */
+        u64 total;                              /* total time need for that job is us */
+        u32 bug;				/* bug counter for negative time delta */
+};
+
+/**
+ * frame statistics
+ */
+struct gtm501l_frame_stats {                 /* frame transfer statistics */
+	spinlock_t lock;                     /* lock for that structure */
+        enum gtm501l_fsequence seq[NR_CPUS];	/* current sequence for each CPU separate */
+        struct gtm501l_jtime idle;              /* timings for idle, just waiting for the application or GTM501L become busy */
+        struct gtm501l_jtime encode;            /* timings for encoding SPI frame */
+        struct gtm501l_jtime transceive;        /* timings for tranceiving SPI frame */
+        struct gtm501l_jtime decode;            /* timings for decoding SPI frame */
+        struct gtm501l_jtime wait;              /* timings for waiting for GTM501L become ready */
+        struct gtm501l_jtime cycle;		/* timings for a SPI frame cycle without idle time */
+        struct kfifo *transmit_id_pipe;		/* fifo pipe frame id to transmit task */
+        struct kfifo *decode_id_pipe;		/* fifo pipe frame id to decode task */
+        struct kfifo *decode_txb_pipe;		/* fifo pipe number of transmit byte to decode task for analysis */
+        struct kfifo *decode_dt_pipe;		/* fifo pipe SPI frame transfer time to decode task for analysis */
+        u32 transmit_id;			/* id and number of transmit SPI frames */
+        u32 receive_id;				/* id and number of received SPI frames */
+        u32 encode_start_id;			/* id and number of started encoded frames */
+        u32 encode_end_id;			/* id and number of finished encoded frames */
+        u32 decode_start_id;			/* id and number of started decoded frames */
+        u32 decode_end_id;			/* id and number of started decoded frames */
+        u32 idles;				/* number of entered idle states */
+        u32 waits;				/* number of entered wait states */
+        u32 max_tx_bytes;                       /* maximum transmitted bytes in a frame */
+        u32 max_rx_bytes;                       /* maximum received bytes in a frame */
+        u64 total_tx_bytes;                     /* total transmitted bytes in a frame for calculating average */
+        u64 total_rx_bytes;                     /* total received bytes in a frame for calculating average */
+        u32 first_tx_bytes;                     /* first transmitted bytes in a frame for calculating average */
+        u32 first_rx_bytes;                     /* first received bytes in a frame for calculating average */
+        u32 max_tx_rate;                        /* maximum transmitted bytes per time rate in bytes/sec */
+        u32 max_rx_rate;                        /* maximum received bytes per time rate in bytes/sec */
+        u32 encode_pass_decode;			/* encode task pass decode task */
+        u32 encode_interrupts_decode;		/* encode task interrupts decode task on the same CPU */
+        u32 decode_pass_encode;			/* decode task pass encode task */
+        u32 decode_interrupts_encode;		/* decode task interrupts encode task on the same CPU */
+        u32 encode_bug;				/* number of counted bugs for encode process */
+        int encode_buffers_used;		/* number of need encode buffers */
+        u32 decode_bug;				/* number of counted bugs for the decode process */
+        int decode_buffers_used;		/* number of need decode buffers */
+	struct dentry *debugfs;                 /* debugfs entry for the frame_stats file */
+};
+
+#endif
+
+struct gtm501_stats_ops {
+	void (*wait_finished)(struct gtm501l_frame_stats *fstats);
+	void (*encode_start_idle_finished)(struct gtm501l_frame_stats *fstats);
+	void (*encode_finished)(struct gtm501l_frame_stats *fstats, unsigned int tx_bytes);
+	void (*transfer_start)(struct gtm501l_frame_stats *fstats);
+	void (*transfer_finished_wait_start)(struct gtm501l_frame_stats *fstats);
+	void (*transfer_decode_start)(struct gtm501l_frame_stats *fstats);
+	void (*decode_finished_may_idle_start)(struct gtm501l_frame_stats *fstats, unsigned int rx_bytes);
+};
+
+/* Prototypes */
+struct gtm501l_device *gtm501l_set_stats_ops(struct gtm501_stats_ops *stats);
+
+#endif
