Index: linux-2.6.33/drivers/spi/Kconfig =================================================================== --- linux-2.6.33.orig/drivers/spi/Kconfig +++ linux-2.6.33/drivers/spi/Kconfig @@ -339,6 +339,10 @@ config SPI_MRST_GTM501 tristate "SPI protocol driver for GTM501l" depends on SPI_MRST +config SPI_IFX_GPS + tristate "SPI protocol driver for IFX HH2 GPS" + 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 @@ -44,6 +44,7 @@ obj-$(CONFIG_SPI_STMP3XXX) += spi_stmp. obj-$(CONFIG_SPI_NUC900) += spi_nuc900.o obj-$(CONFIG_SPI_MRST) += mrst_spi.o obj-$(CONFIG_SPI_MRST_GTM501) += gtm501l_spi.o +obj-$(CONFIG_SPI_IFX_GPS) += hh2serial.o # special build for s3c24xx spi driver with fiq support spi_s3c24xx_hw-y := spi_s3c24xx.o Index: linux-2.6.33/drivers/spi/hh2serial.c =================================================================== --- /dev/null +++ linux-2.6.33/drivers/spi/hh2serial.c @@ -0,0 +1,1572 @@ +/* + * HH2 SPI Serial driver + * + * Copyright (C) 2009 Markus Burvall (Markus.Burvall@swedenconnectivity.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + */ + + +#define DEBUG 1 + +//#define HH2_TTY_ECHO +//#define HH2_TTY_SEND_POLL +//#define HH2_NO_SPI +#define HH2SERIAL_SPI_16BIT +//#define HH2SERIAL_ENABLE_DEBUG +#define HH2SERIAL_SPI_POLL + + +#include +#include +#include + +#include +#include + +#include +#include +#include + +#ifndef HH2_NO_SPI +#include +#include +#endif + +MODULE_AUTHOR("Markus Burvall "); +MODULE_DESCRIPTION("HH2 Serial Driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("hh2serial"); + +#ifdef HH2SERIAL_ENABLE_DEBUG + +#define FUNC_ENTER() do { printk("ENTER: %s\n", __func__); } while (0) + +#else + +#define FUNC_ENTER() + +#endif + + +struct hh2serial_dev { + struct uart_port port; + bool tx_enabled; + bool rx_enabled; + struct spi_device *spi; + + struct task_struct *main_thread; + struct task_struct *poll_thread; + + wait_queue_head_t wq; + atomic_t spi_need_read; + atomic_t tty_need_read; + atomic_t spi_irq_pending; + int mthread_up; +}; + +static const char driver_name[] = "hh2serial"; +static const char tty_dev_name[] = "ttyHH2"; +static struct hh2serial_dev priv0; + + +/* max len for a spi transfer is 18B */ +#define HH2SERIAL_SPI_MAX_BYTES 18 +/* 16 bits / byte + read and write gives 4*18 = 72 */ +#define HH2SERIAL_BUFSIZE 72 + + +#ifdef HH2SERIAL_SPI_POLL +#define HH2SERIAL_POLL_TIMEOUT 100 +#endif + +/* HH2 DATA OPERATIONS */ +#define GPSD_SRREAD 0x80 /* bit 7 */ +#define GPSD_DWRITE 0x40 /* bit 6 */ +#define GPSD_DREAD 0xC0 /* bit 7 and 6 */ +#define GPSD_CRWRITE 0x00 /* All zero */ + +#ifdef HH2SERIAL_SPI_16BIT +/* HH2 DATA OPERATIONS */ +#define GPSD_16BIT_SRREAD 0x8000 /* bit 7 */ +#define GPSD_16BIT_DWRITE 0x4000 /* bit 6 */ +#define GPSD_16BIT_DREAD 0xC000 /* bit 7 and 6 */ +#define GPSD_16BIT_CRWRITE 0x0000 /* All zero */ +#endif + +/* HH2 STATUS REGISTER */ +#define GPSS_TCNT 0x1F /* bits [4..0] */ +#define GPSS_REMPTY 0x20 /* bit 5 */ +#define GPSS_TERR 0x40 /* bit 6 */ +#define GPSS_RERR 0x80 /* bit 7 */ + +/* HH2 CONTROL REGISTER */ +#define GPSC_ENABLE_TCNT_INTR 0x10 /* Enable Rx interrupt */ +#define GPSC_ENABLE_REMPTY_INTR 0x20 /* Enable Tx interrupt */ +#define GPSC_CLEAR_TERR 0x40 /* Clear TERR */ +#define GPSC_CLEAR_RERR 0x80 /* Clear RERR */ +#define GPSC_ENABLE_INTERRUPTS 0x30 /* Enable Interrupts through control register */ +#define GPSC_DISABLE_INTERRUPTS 0x00 /* Disable Interrupts through control register */ + + +/* ************************* */ + +/******************************************************************************* + * FUNCTION: hh2serial_stop_tx + * + * DESCRIPTION: + * + * PARAMETERS: + * + * RETURN: + * + ******************************************************************************/ +static void hh2serial_stop_tx(struct uart_port *port) +{ + struct hh2serial_dev *priv = container_of(port, struct hh2serial_dev, port); + FUNC_ENTER(); + priv->tx_enabled = false; +} + + +/******************************************************************************* + * FUNCTION: hh2serial_spi_get_rx_len + * + * DESCRIPTION: + * + * PARAMETERS: + * + * RETURN: + * + ******************************************************************************/ +#ifndef HH2_NO_SPI +/* Reads status register from HH2 */ +/* Negative for error */ +int hh2serial_spi_get_rx_len(struct hh2serial_dev *hh2serial) +{ + struct spi_device *spi = hh2serial->spi; + int ret; + struct spi_message message; + struct spi_transfer x; + u8 *local_buf; + u8 *buf_ptr; + + FUNC_ENTER(); + + spi_message_init(&message); + memset(&x, 0, sizeof x); +#ifndef HH2SERIAL_SPI_16BIT + x.len = 1; +#else + x.len = 2; +#endif + spi_message_add_tail(&x, &message); + + local_buf = kzalloc((x.len * 2), GFP_KERNEL); + if (!local_buf) + return -ENOMEM; + + +#ifndef HH2SERIAL_SPI_16BIT + local_buf[0] = GPSD_SRREAD; +#else /* if 16 bit, write control to get status */ + local_buf[1] = GPSD_CRWRITE; + local_buf[0] = GPSC_CLEAR_TERR | GPSC_CLEAR_RERR; + /*FIXME if not clearing errors */ + //local_buf[0] = 0; +#endif + x.tx_buf = local_buf; + x.rx_buf = local_buf + x.len; + + x.cs_change = 0; + x.speed_hz = 1562500; + + /* do the i/o */ + ret = spi_sync(spi, &message); + if (ret == 0) + { + + buf_ptr = x.rx_buf; + +#ifdef HH2SERIAL_ENABLE_DEBUG + printk(KERN_INFO "hh2serial RD:%02X, %02X\n", + *buf_ptr, + buf_ptr[1]); +#endif + +#ifndef HH2SERIAL_SPI_16BIT + /* 8 bit First byte is status register */ + /* Available bytes */ + ret = *buf_ptr & GPSS_TCNT; + + /* Check buffer overrun or underrun errors */ + if (*buf_ptr & GPSS_TERR) + printk(KERN_INFO "hh2serial HH2 transmitter underrun!\n"); + + if (*buf_ptr & GPSS_RERR) + printk(KERN_INFO "hh2serial HH2 receiver overrun!\n"); + +#else + /* 16 bit second byte is status register */ + /* Available bytes */ + ret = buf_ptr[1] & GPSS_TCNT; + + /* Check buffer overrun or underrun errors */ + if (buf_ptr[1] & GPSS_TERR) + printk(KERN_INFO "hh2serial HH2 transmitter underrun!\n"); + + if (buf_ptr[1] & GPSS_RERR) + printk(KERN_INFO "hh2serial HH2 receiver overrun!\n"); +#endif + /* Take care of errors */ + /* FIX ME */ + +#ifdef HH2SERIAL_ENABLE_DEBUG + printk(KERN_INFO "hh2serial SR:%02X, rx len %d\n", + buf_ptr[1], + ret); +#endif + } + + kfree(local_buf); + return ret; + +} +#endif + +/******************************************************************************* + * FUNCTION: hh2serial_spi_read + * + * DESCRIPTION: + * + * PARAMETERS: + * + * RETURN: + * + ******************************************************************************/ +#ifndef HH2_NO_SPI +/* Reads maximum 18 bytes of data from SPI buffer */ +int hh2serial_spi_read(struct hh2serial_dev *hh2serial, + u8 *rxbuf, u8 *spiAvailData, unsigned len) +{ + struct spi_device *spi = hh2serial->spi; + int status, available_rd; + struct spi_message message; + struct spi_transfer x; + u8 *local_buf; + u8 *buf_ptr; + unsigned len_inc_hdr; + + FUNC_ENTER(); + /* FIXME check header */ + if ((len * 2) > HH2SERIAL_BUFSIZE || !rxbuf) + return -EINVAL; + + spi_message_init(&message); + memset(&x, 0, sizeof x); + + /* Add header length */ +#ifndef HH2SERIAL_SPI_16BIT + len_inc_hdr = len+1; +#else + len_inc_hdr = len; +#endif + + x.len = len_inc_hdr; + spi_message_add_tail(&x, &message); + + local_buf = kzalloc(HH2SERIAL_BUFSIZE, GFP_KERNEL); + if (!local_buf) + return -ENOMEM; + + /* Add DATA READ as every second byte */ + local_buf[1] = GPSD_DREAD; +#ifdef HH2SERIAL_SPI_16BIT + if (len_inc_hdr > 2) + { + int byte_index = 1; + while (byte_index < len_inc_hdr) + { + local_buf[byte_index] = GPSD_DREAD; + byte_index = byte_index + 2; + } + } + +#endif + + x.tx_buf = local_buf; + x.rx_buf = local_buf + len_inc_hdr; + + + x.cs_change = 0; + x.speed_hz = 1562500; + +#ifdef HH2SERIAL_ENABLE_DEBUG + if (len > 0) + { + int byte_index = 0; + printk(KERN_INFO "hh2serial_spi_read:\n:wr data"); + while (byte_index < len_inc_hdr) + { + printk(KERN_INFO "%02X", (local_buf[byte_index++])); + } + + printk(KERN_INFO "\n"); + + + } +#endif + /* do the i/o */ + status = spi_sync(spi, &message); + if (status == 0) + { + /* First byte of read data */ + buf_ptr = x.rx_buf; + +#ifndef HH2SERIAL_SPI_16BIT + /* 8 bit First byte is status register */ + /* Available bytes */ + available_rd = *buf_ptr & GPSS_TCNT; + + /* Check buffer overrun or underrun errors */ + if (*buf_ptr & GPSS_TERR) + printk(KERN_INFO "hh2serial HH2 transmitter underrun!\n"); + + if (*buf_ptr & GPSS_RERR) + printk(KERN_INFO "hh2serial HH2 receiver overrun!\n"); +#else + /* 16 bit second byte is status register */ + /* Every other byte is status register */ + /* Last status register contains Available bytes at end of op*/ + /* This is status before the last byte is read, so -1 */ + available_rd = (buf_ptr[len_inc_hdr-1] & GPSS_TCNT) - 1; + + /* Check buffer overrun or underrun errors */ + if (buf_ptr[len_inc_hdr-1] & GPSS_TERR) + printk(KERN_INFO "hh2serial HH2 transmitter underrun!\n"); + + if (buf_ptr[len_inc_hdr-1] & GPSS_RERR) + printk(KERN_INFO "hh2serial HH2 receiver overrun!\n"); +#endif + + +#ifdef HH2SERIAL_ENABLE_DEBUG + printk(KERN_INFO "hh2serial_spi_read len inc hdr wr:%d, avail rd %d, cs_change:%d\n", + len_inc_hdr, + available_rd, + x.cs_change); + printk(KERN_INFO "hh2serial_spi_read:%02X, %02X\n", + *buf_ptr, + buf_ptr[1]); + +#endif + + /* Don't copy status byte */ +#ifndef HH2SERIAL_SPI_16BIT + buf_ptr++; +#endif + + *spiAvailData = available_rd; + memcpy(rxbuf, buf_ptr, len); + + /* Print incoming message */ +#ifdef HH2SERIAL_ENABLE_DEBUG + if (len > 0) + { + int byte_index = 0; + printk(KERN_INFO "hh2serial_spi_read:\n:rd data"); + while (byte_index < len) + { + printk(KERN_INFO "%02X", (rxbuf[byte_index++])); + } + printk(KERN_INFO "\n"); + + } +#endif + + } + + kfree(local_buf); + return status; +} +#endif + +/******************************************************************************* + * FUNCTION: hh2serial_spi_write + * + * DESCRIPTION: + * + * PARAMETERS: + * + * RETURN: + * + ******************************************************************************/ +#ifndef HH2_NO_SPI +int hh2serial_spi_write(struct hh2serial_dev *hh2serial, + const u8 *txbuf, u8 *spiAvailData, unsigned len) +{ + struct spi_device *spi = hh2serial->spi; + int status, available_rd; + struct spi_message message; + struct spi_transfer x; + u8 *local_buf; + u8 *buf_ptr; + unsigned len_inc_hdr; + + FUNC_ENTER(); + + if ((len * 2) > HH2SERIAL_BUFSIZE ) + return -EINVAL; + + + spi_message_init(&message); + memset(&x, 0, sizeof x); + + /* Add header length */ +#ifndef HH2SERIAL_SPI_16BIT + len_inc_hdr = len+1; +#else + len_inc_hdr = len; +#endif + + x.len = len_inc_hdr; + spi_message_add_tail(&x, &message); + + /* Allocate and make room for 1 byte header */ + local_buf = kzalloc(HH2SERIAL_BUFSIZE+1, GFP_KERNEL); + if (!local_buf) + return -ENOMEM; + + /* Add write header */ + local_buf[1] = GPSD_DWRITE; + local_buf[0] = txbuf[0]; + + +#ifndef HH2SERIAL_SPI_16BIT + memcpy(&(local_buf[1]), txbuf, len); +#else + if (len_inc_hdr > 2) + { + int byte_index = 2; + while (byte_index < len_inc_hdr) + { + + local_buf[byte_index] = txbuf[byte_index]; + local_buf[byte_index+1] = GPSD_DWRITE; + byte_index = byte_index + 2; + } + } +#endif + + x.tx_buf = local_buf; + x.rx_buf = local_buf +(len_inc_hdr); + + x.cs_change = 0; + x.speed_hz = 1562500; + +#ifdef HH2SERIAL_ENABLE_DEBUG + if (len > 0) + { + int byte_index = 0; + printk(KERN_INFO "hh2serial_spi_write:\n:wr data"); + while (byte_index < len_inc_hdr) + { + printk(KERN_INFO "%02X", (local_buf[byte_index++])); + } + printk(KERN_INFO "\n"); + + + } +#endif + + /* do the i/o */ + status = spi_sync(spi, &message); + if (status == 0) + { + /* read data */ + buf_ptr = x.rx_buf; + +#ifndef HH2SERIAL_SPI_16BIT + /* 8 bit First byte is status register */ + /* Available bytes */ + available_rd = *buf_ptr & GPSS_TCNT; + + /* Check buffer overrun or underrun errors */ + if (*buf_ptr & GPSS_TERR) + printk(KERN_INFO "hh2serial HH2 transmitter underrun!\n"); + + if (*buf_ptr & GPSS_RERR) + printk(KERN_INFO "hh2serial HH2 receiver overrun!\n"); +#else + /* 16 bit second byte is status register */ + /* Available bytes */ + available_rd = buf_ptr[1] & GPSS_TCNT; + + /* Check buffer overrun or underrun errors */ + if (buf_ptr[1] & GPSS_TERR) + printk(KERN_INFO "hh2serial HH2 transmitter underrun!\n"); + + if (buf_ptr[1] & GPSS_RERR) + printk(KERN_INFO "hh2serial HH2 receiver overrun!\n"); +#endif + + +#ifdef HH2SERIAL_ENABLE_DEBUG + printk(KERN_INFO "hh2serial_spi_write:%02X, %02X\n", + *buf_ptr, + buf_ptr[1]); + + printk(KERN_INFO "hh2serial_spi_write: wr:%d, avail rd %d\n", + len, + available_rd); +#endif + + *spiAvailData = available_rd; + + + } + + + + kfree(local_buf); + return status; +} +#endif + +/******************************************************************************* + * FUNCTION: hh2serial_write2tty + * + * DESCRIPTION: + * + * PARAMETERS: + * + * RETURN: + * + ******************************************************************************/ +static void hh2serial_write2tty( + struct hh2serial_dev *priv, unsigned char *str, int len) +{ + struct uart_port *port = &priv->port; + struct tty_struct *tty; + int usable; + + FUNC_ENTER(); + + /* if uart is not opened, will just return */ + if (!port->state) + return; + + tty = port->state->port.tty; + if (!tty) + return; /* receive some char before the tty is opened */ + + /* MRB could lock forever if no space in tty buffer */ + while (len) { + usable = tty_buffer_request_room(tty, len); + if (usable) { +#ifdef HH2SERIAL_ENABLE_DEBUG + printk(KERN_INFO "hh2serial_output_tty buf space: %d\n", usable); +#endif + tty_insert_flip_string(tty, str, usable); + str += usable; + port->icount.rx += usable; + tty_flip_buffer_push(tty); + } + len -= usable; + } +} + +/******************************************************************************* + * FUNCTION: hh2serial_write_circ_buf2spi + * + * DESCRIPTION: + * + * PARAMETERS: + * + * RETURN: + * + ******************************************************************************/ +#ifndef HH2_NO_SPI +static inline void hh2serial_write_circ_buf2spi(struct hh2serial_dev *priv, + struct circ_buf *xmit) +{ + int len, left = 0; +#ifndef HH2SERIAL_SPI_16BIT + u8 obuf[HH2SERIAL_SPI_MAX_BYTES], ibuf[HH2SERIAL_SPI_MAX_BYTES]; +#else + u16 obuf[HH2SERIAL_SPI_MAX_BYTES], ibuf[HH2SERIAL_SPI_MAX_BYTES]; +#endif + u8 rxlen; + u8 valid_str[HH2SERIAL_SPI_MAX_BYTES]; + + int i, j; + + FUNC_ENTER(); + + while (!uart_circ_empty(xmit)) { + /* + printk(KERN_INFO "MrB set CR get SR: %d\n", + hh2serial_spi_get_rx_len(priv)); + */ + + left = uart_circ_chars_pending(xmit); +#ifdef HH2SERIAL_ENABLE_DEBUG + printk(KERN_INFO "Bytes in circ buffer: %d\n", left); +#endif + while (left) { + /* MrB Change below to 1 and word length to 16 to write 16 bit + word by word */ +#ifndef HH2SERIAL_SPI_16BIT + len = (left >= HH2SERIAL_SPI_MAX_BYTES) ? HH2SERIAL_SPI_MAX_BYTES : left; +#else + len = (left >= HH2SERIAL_SPI_MAX_BYTES) ? HH2SERIAL_SPI_MAX_BYTES : left; +#endif + + memset(obuf, 0, len); + memset(ibuf, 0, len); + for (i = 0; i < len; i++) { + + obuf[i] = (u8)xmit->buf[xmit->tail]; + + xmit->tail = (xmit->tail + 1) & + (UART_XMIT_SIZE - 1); + } +#ifndef HH2SERIAL_SPI_16BIT + + hh2serial_spi_write(priv, (u8 *)obuf, + &rxlen, len); + +#else + /* len * 2 since 16 bits instead of 8 bits */ + hh2serial_spi_write(priv, (u8 *)obuf, + &rxlen, len*2); + +#endif + left -= len; + } +#ifdef HH2SERIAL_ENABLE_DEBUG + printk(KERN_INFO "hh2serial: Bytes avail to read: %d\n", rxlen); +#endif + /* Read if available bytes */ + /* FIXME: Could add a maximum read loop here */ + while (rxlen > 0) + { + + len = rxlen; +#ifndef HH2SERIAL_SPI_16BIT + hh2serial_spi_read(priv, (u8 *)ibuf, &rxlen, len); +#else + hh2serial_spi_read(priv, (u8 *)ibuf, &rxlen, len*2); +#endif + + for (i = 0, j = 0; i < len; i++) { + valid_str[j++] = (u8)(ibuf[i]); + } + + if (j) + hh2serial_write2tty(priv, valid_str, j); + + priv->port.icount.tx += len; + } + } +} +#endif + + +/******************************************************************************* + * FUNCTION: hh2serial_handle_tty_input + * + * DESCRIPTION: + * + * PARAMETERS: + * + * RETURN: + * + ******************************************************************************/ +static void hh2serial_handle_tty_input(struct hh2serial_dev *priv) +{ + struct uart_port *port = &priv->port; + struct circ_buf *xmit = &port->state->xmit; + + FUNC_ENTER(); + + if (uart_circ_empty(xmit) || uart_tx_stopped(port)) + return; +#ifndef HH2_NO_SPI + hh2serial_write_circ_buf2spi(priv, xmit); +#endif + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); + + if (uart_circ_empty(xmit)) + hh2serial_stop_tx(port); +} + +/******************************************************************************* + * FUNCTION: hh2serial_transfer_spi2tty + * + * DESCRIPTION: + * + * PARAMETERS: + * + * RETURN: + * + ******************************************************************************/ +static void hh2serial_transfer_spi2tty(struct hh2serial_dev *priv) +{ + int loop = 10, len; + int i, j; + u8 valid_str[HH2SERIAL_SPI_MAX_BYTES], rxlen = 0; +#ifndef HH2SERIAL_SPI_16BIT + u8 ibuf[HH2SERIAL_SPI_MAX_BYTES]; +#else + u16 ibuf[HH2SERIAL_SPI_MAX_BYTES]; +#endif + + FUNC_ENTER(); + + rxlen = hh2serial_spi_get_rx_len(priv); + + /* FIXME No of loops to be investigated */ + while (rxlen > 0 && loop > 0) + { + + len = rxlen; +#ifndef HH2SERIAL_SPI_16BIT + hh2serial_spi_read(priv, (u8 *)ibuf, &rxlen, len); +#else + hh2serial_spi_read(priv, (u8 *)ibuf, &rxlen, len*2); +#endif + + for (i = 0, j = 0; i < len; i++) { + valid_str[j++] = (u8)(ibuf[i]); + } + + if (j) + hh2serial_write2tty(priv, valid_str, j); + + priv->port.icount.tx += len; + + loop--; + } + +} + + +/******************************************************************************* + * FUNCTION: hh2serial_main_thread + * + * DESCRIPTION: + * + * PARAMETERS: + * + * RETURN: + * + ******************************************************************************/ +static int hh2serial_main_thread(void *_priv) +{ + struct hh2serial_dev *priv = _priv; + wait_queue_head_t *wq = &priv->wq; + + int ret = 0; + +#ifdef HH2SERIAL_ENABLE_DEBUG + printk(KERN_INFO "hh2serial: start main thread\n"); +#endif + init_waitqueue_head(wq); + + do { + //udelay(delay); + wait_event_interruptible(*wq, (atomic_read(&priv->spi_irq_pending) || + atomic_read(&priv->spi_need_read) || + atomic_read(&priv->tty_need_read) || + kthread_should_stop())); + + priv->mthread_up = 1; + + /* tty has data to be read */ + if (atomic_read(&priv->tty_need_read)) { + atomic_set(&priv->tty_need_read, 0); + /* Read from tty send to spi */ +#ifdef HH2SERIAL_ENABLE_DEBUG + printk(KERN_INFO "hh2serial: Read from tty send to spi\n"); +#endif + /* Read from tty send to spi */ + /* Receive data from spi send to UART */ + + hh2serial_handle_tty_input(priv); + + } + +#ifdef HH2SERIAL_SPI_POLL + if (atomic_read(&priv->spi_need_read)) { + atomic_set(&priv->spi_need_read, 0); + /* Read from SPI send to UART */ +#ifdef HH2SERIAL_ENABLE_DEBUG + printk(KERN_INFO "hh2serial: Read from SPI send to UART\n"); +#endif +#ifndef HH2_TTY_SEND_POLL + hh2serial_transfer_spi2tty(priv); +#else + if (priv->tx_enabled) { + struct uart_port *port = &priv->port; +#ifdef HH2SERIAL_ENABLE_DEBUG + printk("TX enabled!\n"); +#endif + spin_lock_irqsave(&port->lock, flags); + + + if (priv->rx_enabled) { +#ifdef HH2SERIAL_ENABLE_DEBUG + printk(KERN_INFO "RX enabled!\n"); +#endif + hh2serial_write2tty(priv, "testar", 6); + } + + + spin_unlock_irqrestore(&port->lock, flags); + } +#endif /* HH2_TTY_SEND_POLL */ + + } +#endif + + + + if (atomic_read(&priv->spi_irq_pending)) { + atomic_set(&priv->spi_irq_pending, 0); + /* Read from SPI send to UART */ +#ifdef HH2SERIAL_ENABLE_DEBUG + printk(KERN_INFO "hh2serial: Read from SPI send to UART\n"); +#endif + } + + + priv->mthread_up = 0; + } while (!kthread_should_stop()); +#ifdef HH2SERIAL_ENABLE_DEBUG + printk(KERN_INFO "hh2serial: stopped main thread\n"); +#endif + return ret; +} + +/******************************************************************************* + * FUNCTION: hh2serial_poll_thread + * + * DESCRIPTION: + * + * PARAMETERS: + * + * RETURN: + * + ******************************************************************************/ +#ifdef HH2SERIAL_SPI_POLL +static int hh2serial_poll_thread(void *_priv) +{ + + int ret = 0; + struct hh2serial_dev *priv = _priv; + +#ifdef HH2SERIAL_ENABLE_DEBUG + printk(KERN_INFO "hh2serial: start poll thread\n"); +#endif + do { + //udelay(delay); + + if (HH2SERIAL_POLL_TIMEOUT > 999) + ssleep(HH2SERIAL_POLL_TIMEOUT/1000); + else + msleep(HH2SERIAL_POLL_TIMEOUT); +#ifdef HH2SERIAL_ENABLE_DEBUG + printk(KERN_INFO "hh2serial: poll\n"); +#endif + if (!priv->mthread_up) + { + /* Send poll event to main */ + if (!atomic_read(&priv->spi_need_read)) { + atomic_set(&priv->spi_need_read, 1); + wake_up_process(priv->main_thread); + } + } + + } while (!kthread_should_stop()); +#ifdef HH2SERIAL_ENABLE_DEBUG + printk(KERN_INFO "hh2serial: stopped poll thread\n"); +#endif + return ret; +} +#endif /* #ifdef HH2SERIAL_SPI_POLL */ + +/******************************************************************************* + * FUNCTION: hh2serial_tx_empty + * + * DESCRIPTION: + * + * PARAMETERS: + * + * RETURN: + * + ******************************************************************************/ +static unsigned int hh2serial_tx_empty(struct uart_port *port) +{ + FUNC_ENTER(); + return TIOCSER_TEMT; +} + +/******************************************************************************* + * FUNCTION: hh2serial_set_mctrl + * + * DESCRIPTION: + * + * PARAMETERS: + * + * RETURN: + * + ******************************************************************************/ +static void hh2serial_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + FUNC_ENTER(); + +#ifdef HH2SERIAL_ENABLE_DEBUG + printk("MCTRL RTS: %d\n", mctrl & TIOCM_RTS); + printk("MCTRL DTR: %d\n", mctrl & TIOCM_DTR); + printk("MCTRL OUT1: %d\n", mctrl & TIOCM_OUT1); + printk("MCTRL OUT2: %d\n", mctrl & TIOCM_OUT2); + printk("MCTRL LOOP: %d\n", mctrl & TIOCM_LOOP); +#endif +} + +/******************************************************************************* + * FUNCTION: hh2serial_get_mctrl + * + * DESCRIPTION: + * + * PARAMETERS: + * + * RETURN: + * + ******************************************************************************/ +static unsigned int hh2serial_get_mctrl(struct uart_port *port) +{ + FUNC_ENTER(); + return TIOCM_CAR | TIOCM_DSR | TIOCM_CTS; +} + + +/******************************************************************************* + * FUNCTION: hh2serial_tx_chars + * + * DESCRIPTION: + * + * PARAMETERS: + * + * RETURN: + * + ******************************************************************************/ +static void hh2serial_tx_chars(struct uart_port *port) +{ +#ifndef HH2_TTY_ECHO + struct hh2serial_dev *priv = container_of(port, struct hh2serial_dev, port); + + FUNC_ENTER(); + + if (priv->tx_enabled) { + + /* if writing to SPI enabled */ + + /* Send message to main thread to read from tty send to SPI */ + /* Send poll event to main */ + if (!atomic_read(&priv->tty_need_read)) { + atomic_set(&priv->tty_need_read, 1); + wake_up_process(priv->main_thread); + } + + + } + +#else + struct hh2serial_dev *priv = container_of(port, struct hh2serial_dev, port); + struct circ_buf *xmit = &port->state->xmit; + + + + struct uart_port *recv_port = &priv->port; + struct tty_struct *recv_tty; + + unsigned long flags; + char ch; + + FUNC_ENTER(); + + if (priv->tx_enabled) { +#ifdef HH2SERIAL_ENABLE_DEBUG + printk("TX enabled!\n"); +#endif + //spin_lock_irqsave(&other_port->lock, flags); + if (priv->rx_enabled) { +#ifdef HH2SERIAL_ENABLE_DEBUG + printk("RX enabled!\n"); +#endif + + recv_tty = recv_port->state->port.tty; + + if (port->x_char) { +#ifdef HH2SERIAL_ENABLE_DEBUG + printk("One char %c!\n", port->x_char); +#endif + tty_insert_flip_char(recv_tty, port->x_char, TTY_NORMAL); + tty_flip_buffer_push(recv_tty); + port->icount.tx++; + port->x_char = 0; + return; + } + + if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { +#ifdef HH2SERIAL_ENABLE_DEBUG + pr_debug("STOP TX_CHARS 1\n"); +#endif + hh2serial_stop_tx(port); + return; + } + + while (!uart_circ_empty(xmit)) { + + ch = xmit->buf[xmit->tail]; + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); +#ifdef HH2SERIAL_ENABLE_DEBUG + printk("Loop one char %c!\n", ch); +#endif + tty_insert_flip_char(recv_tty, ch, TTY_NORMAL); + tty_flip_buffer_push(recv_tty); + port->icount.tx++; + } + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + { +#ifdef HH2SERIAL_ENABLE_DEBUG + printk("Uart wakeup!\n"); +#endif + uart_write_wakeup(port); + } + + if (uart_circ_empty(xmit)) { +#ifdef HH2SERIAL_ENABLE_DEBUG + pr_debug("STOP TX_CHARS 2\n"); +#endif + hh2serial_stop_tx(port); + } + } + else + { +#ifdef HH2SERIAL_ENABLE_DEBUG + printk("Other port disabled!\n"); +#endif + } + //spin_unlock_irqrestore(&priv->other_priv->port.lock, flags); + } + +#endif + +} + +/******************************************************************************* + * FUNCTION: hh2serial_start_tx + * + * DESCRIPTION: + * + * PARAMETERS: + * + * RETURN: + * + ******************************************************************************/ +static void hh2serial_start_tx(struct uart_port *port) +{ + struct hh2serial_dev *priv = container_of(port, struct hh2serial_dev, port); + FUNC_ENTER(); + priv->tx_enabled = true; + + hh2serial_tx_chars(port); +} + +/******************************************************************************* + * FUNCTION: hh2serial_stop_rx + * + * DESCRIPTION: + * + * PARAMETERS: + * + * RETURN: + * + ******************************************************************************/ +static void hh2serial_stop_rx(struct uart_port *port) +{ + struct hh2serial_dev *priv = container_of(port, struct hh2serial_dev, port); + FUNC_ENTER(); + priv->rx_enabled = false; +} + +/******************************************************************************* + * FUNCTION: hh2serial_enable_ms + * + * DESCRIPTION: + * + * PARAMETERS: + * + * RETURN: + * + ******************************************************************************/ +static void hh2serial_enable_ms(struct uart_port *port) +{ + FUNC_ENTER(); +} + +/******************************************************************************* + * FUNCTION: hh2serial_break_ctl + * + * DESCRIPTION: + * + * PARAMETERS: + * + * RETURN: + * + ******************************************************************************/ +static void hh2serial_break_ctl(struct uart_port *port, int break_state) +{ + FUNC_ENTER(); +} + +/******************************************************************************* + * FUNCTION: hh2serial_startup + * + * DESCRIPTION: + * + * PARAMETERS: + * + * RETURN: + * + ******************************************************************************/ +static int hh2serial_startup(struct uart_port *port) +{ + struct hh2serial_dev *priv = container_of(port, struct hh2serial_dev, port); + FUNC_ENTER(); + +#ifdef HH2SERIAL_SPI_POLL + priv->poll_thread = kthread_run(hh2serial_poll_thread, + priv, "hh2serial_poll"); + if (IS_ERR(priv->poll_thread)) { + printk(KERN_INFO "hh2serial Failed to start poll thread: %ld", + PTR_ERR(priv->poll_thread)); + } +#endif + + spin_lock(&port->lock); + priv->rx_enabled = true; + spin_unlock(&port->lock); + return 0; +} + +/******************************************************************************* + * FUNCTION: hh2serial_shutdown + * + * DESCRIPTION: + * + * PARAMETERS: + * + * RETURN: + * + ******************************************************************************/ +static void hh2serial_shutdown(struct uart_port *port) +{ +#ifdef HH2SERIAL_SPI_POLL + struct hh2serial_dev *priv = container_of(port, struct hh2serial_dev, port); +#endif + FUNC_ENTER(); +#ifdef HH2SERIAL_SPI_POLL + if (priv->poll_thread) + kthread_stop(priv->poll_thread); +#endif +} + +/******************************************************************************* + * FUNCTION: hh2serial_set_termios + * + * DESCRIPTION: + * + * PARAMETERS: + * + * RETURN: + * + ******************************************************************************/ +static void hh2serial_set_termios(struct uart_port *port, + struct ktermios *termios, + struct ktermios *old) +{ + FUNC_ENTER(); + + switch (termios->c_cflag & CSIZE) { + case CS5: + pr_debug("CS5: data bits 5\n"); + break; + case CS6: + pr_debug("CS6: data bits 6\n"); + break; + case CS7: + pr_debug("CS7: data bits 7\n"); + break; + case CS8: + pr_debug("CS8: data bits 8\n"); + break; + default: + pr_debug("CS: Unknown\n"); + break; + } + + if (termios->c_cflag & PARENB) { + if (termios->c_cflag & PARODD) + pr_debug("PARITY ODD\n"); + else + pr_debug("PARITY EVEN\n"); + } else { + pr_debug("PARITY NONE\n"); + } + + if (termios->c_cflag & CSTOPB) + pr_debug("STOP BITS 2\n"); + else + pr_debug("STOP BITS 1\n"); + + if (termios->c_cflag & CRTSCTS) + pr_debug("RTS CTS ENABLED\n"); + else + pr_debug("RTS CTS DISABLED\n"); +} + +/******************************************************************************* + * FUNCTION: hh2serial_type + * + * DESCRIPTION: + * + * PARAMETERS: + * + * RETURN: + * + ******************************************************************************/ +static const char *hh2serial_type(struct uart_port *port) +{ + FUNC_ENTER(); + return "VUART"; +} + +/******************************************************************************* + * FUNCTION: hh2serial_request_port + * + * DESCRIPTION: + * + * PARAMETERS: + * + * RETURN: + * + ******************************************************************************/ +static int hh2serial_request_port(struct uart_port *port) +{ + FUNC_ENTER(); + return 0; +} + +/******************************************************************************* + * FUNCTION: hh2serial_config_port + * + * DESCRIPTION: + * + * PARAMETERS: + * + * RETURN: + * + ******************************************************************************/ +static void hh2serial_config_port(struct uart_port *port, int flags) +{ + FUNC_ENTER(); + + if (flags & UART_CONFIG_TYPE) + port->type = PORT_16550A; +} + +/******************************************************************************* + * FUNCTION: hh2serial_release_port + * + * DESCRIPTION: + * + * PARAMETERS: + * + * RETURN: + * + ******************************************************************************/ +static void hh2serial_release_port(struct uart_port *port) +{ + FUNC_ENTER(); +} + +/******************************************************************************* + * FUNCTION: hh2serial_verify_port + * + * DESCRIPTION: + * + * PARAMETERS: + * + * RETURN: + * + ******************************************************************************/ +static int hh2serial_verify_port(struct uart_port *port, struct serial_struct *ser) +{ + FUNC_ENTER(); + return 0; +} + +static struct uart_ops hh2serial_uart_ops = { + .tx_empty = hh2serial_tx_empty, + .set_mctrl = hh2serial_set_mctrl, + .get_mctrl = hh2serial_get_mctrl, + .stop_tx = hh2serial_stop_tx, + .start_tx = hh2serial_start_tx, + .stop_rx = hh2serial_stop_rx, + .enable_ms = hh2serial_enable_ms, + .break_ctl = hh2serial_break_ctl, + .startup = hh2serial_startup, + .shutdown = hh2serial_shutdown, + .set_termios = hh2serial_set_termios, + .type = hh2serial_type, + .release_port = hh2serial_release_port, + .request_port = hh2serial_request_port, + .config_port = hh2serial_config_port, + .verify_port = hh2serial_verify_port, +}; + +#ifndef HH2_NO_SPI +/* pure SPI related functions */ +/******************************************************************************* + * FUNCTION: serial_hh2serial_suspend + * + * DESCRIPTION: + * + * PARAMETERS: + * + * RETURN: + * + ******************************************************************************/ +static int serial_hh2serial_suspend(struct spi_device *spi, pm_message_t state) +{ + FUNC_ENTER(); + return 0; +} + +/******************************************************************************* + * FUNCTION: serial_hh2serial_resume + * + * DESCRIPTION: + * + * PARAMETERS: + * + * RETURN: + * + ******************************************************************************/ +static int serial_hh2serial_resume(struct spi_device *spi) +{ + FUNC_ENTER(); + return 0; +} + + +static struct mrst_spi_chip hh2spi0 = { + .poll_mode = 1, + .enable_dma = 0, + .type = SPI_FRF_SPI, +}; + + +/******************************************************************************* + * FUNCTION: serial_hh2serial_probe + * + * DESCRIPTION: + * + * PARAMETERS: + * + * RETURN: + * + ******************************************************************************/ +static int serial_hh2serial_probe(struct spi_device *spi) +{ + FUNC_ENTER(); +#ifndef HH2_NO_SPI + + /* set spi info */ + spi->mode = SPI_MODE_0; +#ifndef HH2SERIAL_SPI_16BIT + spi->bits_per_word = 8; /* HH2 uses 8 bits */ +#else + spi->bits_per_word = 16; /* HH2 uses 8 bits, test with 16, sends byte by byte */ +#endif + + spi->controller_data = &hh2spi0; + + spi_setup(spi); + priv0.spi = spi; + atomic_set(&priv0.spi_irq_pending, 0); +#endif + + + return 0; + + +} + +/******************************************************************************* + * FUNCTION: hh2serial_remove + * + * DESCRIPTION: + * + * PARAMETERS: + * + * RETURN: + * + ******************************************************************************/ +static int hh2serial_remove(struct spi_device *dev) +{ + FUNC_ENTER(); + + return 0; +} + + +static struct spi_driver spi_hh2serial_driver = { + .driver = { + .name = "spi_ifx_gps", + //.name = "spi_flash", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, + .probe = serial_hh2serial_probe, + .remove = __devexit_p(hh2serial_remove), + .suspend = serial_hh2serial_suspend, + .resume = serial_hh2serial_resume, +}; + +#endif + +static struct uart_driver hh2serial_driver = { + .owner = THIS_MODULE, + .driver_name = driver_name, + .dev_name = tty_dev_name, + .major = 240, + .minor = 0, + .nr = 1, +}; + +/******************************************************************************* + * FUNCTION: __init + * + * DESCRIPTION: + * + * PARAMETERS: + * + * RETURN: + * + ******************************************************************************/ +static int __init +hh2serial_init (void) +{ + int ret; + + ret = uart_register_driver(&hh2serial_driver); + + if (ret) { + pr_err("%s: could not register UART driver\n", driver_name); + goto out_register_driver; + } + + memset(&priv0, sizeof(struct hh2serial_dev), 0); + priv0.port.line = 0; + priv0.port.ops = &hh2serial_uart_ops; + priv0.port.type = PORT_16550A; + spin_lock_init(&priv0.port.lock); + + ret = uart_add_one_port(&hh2serial_driver, &priv0.port); + + if (ret) { + pr_err("%s: could not add port hh2serial0\n", driver_name); + goto out_add_port0; + } + + atomic_set(&priv0.spi_need_read, 0); + atomic_set(&priv0.tty_need_read, 0); + atomic_set(&priv0.spi_irq_pending, 0); + + + +#ifndef HH2_NO_SPI + /* Register SPI device driver*/ + ret = spi_register_driver(&spi_hh2serial_driver); + if (ret) + { + pr_err("%s: could not register driver spi_hh2serial_driver\n", driver_name); + goto out_add_spi; + } +#endif + + + priv0.main_thread = kthread_run(hh2serial_main_thread, + &priv0, "hh2serial_main"); + if (IS_ERR(priv0.main_thread)) { + ret = PTR_ERR(priv0.main_thread); + goto err_kthread; + } + + + + printk ("Module %s loaded\n", driver_name); + return 0; + +err_kthread: + +#ifndef HH2_NO_SPI +out_add_spi: + uart_remove_one_port(&hh2serial_driver, &priv0.port); +#endif +out_add_port0: + uart_unregister_driver(&hh2serial_driver); +out_register_driver: + return ret; +} + +/******************************************************************************* + * FUNCTION: __exit + * + * DESCRIPTION: + * + * PARAMETERS: + * + * RETURN: + * + ******************************************************************************/ +static void __exit +hh2serial_exit (void) +{ + if (priv0.main_thread) + kthread_stop(priv0.main_thread); + +#ifndef HH2_NO_SPI + /* unregister SPI driver */ + spi_unregister_driver(&spi_hh2serial_driver); +#endif + uart_remove_one_port(&hh2serial_driver, &priv0.port); + + uart_unregister_driver(&hh2serial_driver); + printk ("Module %s removed\n", driver_name); +} + + + +module_init(hh2serial_init); +module_exit(hh2serial_exit); Index: linux-2.6.33/drivers/misc/intel_mrst.c =================================================================== --- linux-2.6.33.orig/drivers/misc/intel_mrst.c +++ linux-2.6.33/drivers/misc/intel_mrst.c @@ -131,9 +131,11 @@ static int intel_mrst_bringup_8688_sdio2 { unsigned int temp = 0; - /* Register 0xf4 has 2 GPIO lines connected to the MRVL 8688: + /* Register 0xf4 has 4 GPIO lines connected to the MRVL 8688 * IFX GPS: * bit 4: PDn - * bit 3: WiFi RESETn */ + * bit 3: WiFi RESETn + * bit 2: GPS RESET_N + * bit 1: GPS PD_N*/ intel_mrst_pmic_read(0xf4, &temp); temp = temp|0x8; @@ -142,6 +144,12 @@ static int intel_mrst_bringup_8688_sdio2 temp = temp|0x10; intel_mrst_pmic_write(0xf4, temp); + temp = temp|0x04; + intel_mrst_pmic_write(0xf4, temp); + + temp = temp|0x02; + intel_mrst_pmic_write(0xf4, temp); + return 0; } @@ -187,10 +195,10 @@ static int __init intel_mrst_module_init /* We only need the following PMIC register initializations if * we are using the Marvell 8688 WLAN card on the SDIO2 port */ -#ifdef CONFIG_8688_RC +#if defined(CONFIG_8688_RC) || defined(CONFIG_LIBERTAS_SDIO) || defined(CONFIG_SPI_IFX_GPS) printk(KERN_INFO "intel_mrst_module_init: bringing up power for " - "8688 WLAN on SDIO2...\n"); + "8688 WLAN on SDIO2 & IFX GPS over SPI...\n"); ret = intel_mrst_bringup_8688_sdio2(); #endif /* CONFIG_8688_RC */