1649 lines
42 KiB
Diff
1649 lines
42 KiB
Diff
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 <linux/kernel.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/init.h>
|
|
+
|
|
+#include <linux/serial.h>
|
|
+#include <linux/serial_core.h>
|
|
+
|
|
+#include <linux/kthread.h>
|
|
+#include <linux/delay.h>
|
|
+#include <asm/atomic.h>
|
|
+
|
|
+#ifndef HH2_NO_SPI
|
|
+#include <linux/spi/spi.h>
|
|
+#include <linux/spi/mrst_spi.h>
|
|
+#endif
|
|
+
|
|
+MODULE_AUTHOR("Markus Burvall <Markus.Burvall@swedenconnectivity.com>");
|
|
+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 */
|