diff options
author | Joshua Lock <josh@linux.intel.com> | 2010-05-18 14:51:13 +0100 |
---|---|---|
committer | Joshua Lock <josh@linux.intel.com> | 2010-05-19 12:20:16 +0100 |
commit | 5e8c7c54a9b297dae0081dd19a7bb94e23040a3d (patch) | |
tree | 948e3642c1bf426870b83c72c68c997dce66766c /meta-moblin/packages/linux/linux-moblin-2.6.33.2/linux-2.6.34-moorestown-gtm501l-driver-1.2.patch | |
parent | 5e07bc91281969d54896dd0a13e3d6134e432027 (diff) | |
download | openembedded-core-5e8c7c54a9b297dae0081dd19a7bb94e23040a3d.tar.gz openembedded-core-5e8c7c54a9b297dae0081dd19a7bb94e23040a3d.tar.bz2 openembedded-core-5e8c7c54a9b297dae0081dd19a7bb94e23040a3d.zip |
linux-moblin: add 2.6.33.2 kernel from MeeGo 1.0
Signed-off-by: Joshua Lock <josh@linux.intel.com>
Diffstat (limited to 'meta-moblin/packages/linux/linux-moblin-2.6.33.2/linux-2.6.34-moorestown-gtm501l-driver-1.2.patch')
-rw-r--r-- | meta-moblin/packages/linux/linux-moblin-2.6.33.2/linux-2.6.34-moorestown-gtm501l-driver-1.2.patch | 2395 |
1 files changed, 2395 insertions, 0 deletions
diff --git a/meta-moblin/packages/linux/linux-moblin-2.6.33.2/linux-2.6.34-moorestown-gtm501l-driver-1.2.patch b/meta-moblin/packages/linux/linux-moblin-2.6.33.2/linux-2.6.34-moorestown-gtm501l-driver-1.2.patch new file mode 100644 index 0000000000..206e49ed6f --- /dev/null +++ b/meta-moblin/packages/linux/linux-moblin-2.6.33.2/linux-2.6.34-moorestown-gtm501l-driver-1.2.patch @@ -0,0 +1,2395 @@ +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, >m501l_gpio_operations); ++ debugfs_create_file("pmic", S_IFREG | S_IRUGO, ++ gtm_dev->debugfs, NULL, >m501l_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 *)>m_dev->flags); ++#ifdef DEBUG ++ debugfs_create_x32("debug", S_IFREG | S_IRUGO, ++ gtm_dev->debugfs, >m501l_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(®_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 *)(>m_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, >m_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, >m_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(>m_dev->timer)) { ++ gtm_dev->timer.expires = jiffies + GTM501L_BACKOFF_TIMER; ++ gtm_dev->timer.function = gtm501l_timer; ++ gtm_dev->timer.data = (unsigned long)>m_dev->io_work_tasklet; ++ add_timer(>m_dev->timer); ++ } ++ } ++ else ++ tasklet_hi_schedule(>m_dev->io_work_tasklet); ++ } ++ else set_bit(GTM501L_STATE_IO_READY, >m_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, >m_dev->flags)) ++ return; ++ ++ if (!test_and_set_bit(GTM501L_STATE_IO_IN_PROGRESS, >m_dev->flags)) { ++ gtm501l_prepare_tx_buffer(gtm_dev); ++ ++ if(gtm_dev->stats) gtm_dev->stats->transfer_start(gtm_dev->frame_stats); ++ ++ spi_message_init(>m_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(>m_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(>m_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(>m_dev->spi_xfer, >m_dev->spi_msg); ++ ++ retval = spi_async(gtm_dev->spi_dev, >m_dev->spi_msg); ++ ++ if (retval) { ++ dprintk(DEBUG_SPI, "ERROR: spi_async failed (%d)\n", retval); ++ clear_bit(GTM501L_STATE_IO_IN_PROGRESS, ++ >m_dev->flags); ++ tasklet_hi_schedule(>m_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, >m501l_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, ++ >m_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(>m_ser->throttle_fifo_lock); ++ lockdep_set_class_and_subclass(>m_ser->throttle_fifo_lock, >m501l_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(>m_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, >m_dev->flags)) ++ return; ++ ++ if (!gtm_dev->spi_msg.status) { ++#if 0 ++ dma_unmap_single(>m_dev->spi_dev->dev, ++ gtm_dev->tx_dma[(gtm_dev->tx_buffer_used + 1) % 2], ++ GTM501L_TRANSFER_SIZE, DMA_TO_DEVICE); ++ dma_unmap_single(>m_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, >m_dev->flags); ++ ++ //gtm501l_prepare_tx_buffer(gtm_dev); ++ ++ if(test_and_clear_bit(GTM501L_STATE_IO_READY, >m_dev->flags)) ++ tasklet_hi_schedule(>m_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(>m_dev->timer)) { ++ del_timer_sync(>m_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(>m_dev->timer)) { ++ del_timer_sync(>m_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(>m_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(>m_dev->io_work_tasklet, ++ (void (*)(unsigned long))gtm501l_io, ++ (unsigned long)gtm_dev); ++ ++ init_timer(>m_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(>m_dev->ref, gtm501l_free_device); ++ func_exit(); ++ return -EIO; ++ } ++ ++ set_bit(GTM501L_STATE_PRESENT, >m_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(>m_dev->timer); ++ ++ clear_bit(GTM501L_STATE_PRESENT, >m_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(>m_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(>m501l_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, >m501l_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(>m501l_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 = >m501l_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 |