sensors: brcm: bbdpl2: add new version (v2) of the BCM4773 sensorhub 34/84534/8
authorHoegeun Kwon <hoegeun.kwon@samsung.com>
Thu, 18 Aug 2016 08:46:19 +0000 (17:46 +0900)
committerHoegeun Kwon <hoegeun.kwon@samsung.com>
Wed, 24 Aug 2016 08:27:24 +0000 (17:27 +0900)
Commit, "sensors: add support for the BCM 4773 sensorhub" has
added support for the BCM4773. Since then the firmware interface
has been updated to a new version: version 2.

Add the second version of the BCM4773 sensorhub and rename the
previous version configuration flags to VER1.

Change-Id: Ie40d4b41fe4c3491567246392d2fee1d7c9cad48
Signed-off-by: Hoegeun Kwon <hoegeun.kwon@samsung.com>
Signed-off-by: Andi Shyti <andi.shyti@samsung.com>
drivers/sensors/brcm/Kconfig
drivers/sensors/brcm/Makefile
drivers/sensors/brcm/bbdpl2/Kconfig [new file with mode: 0644]
drivers/sensors/brcm/bbdpl2/Makefile [new file with mode: 0644]
drivers/sensors/brcm/bbdpl2/bbd.c [new file with mode: 0644]
drivers/sensors/brcm/bbdpl2/bbd.h [new file with mode: 0644]
drivers/sensors/brcm/bbdpl2/bbd_rpc_lh.c [new file with mode: 0644]
drivers/sensors/brcm/bbdpl2/bcm_dbg.c [new file with mode: 0644]
drivers/sensors/brcm/bbdpl2/bcm_gps_i2c.c [new file with mode: 0644]
drivers/sensors/brcm/bbdpl2/bcm_gps_i2c.h [new file with mode: 0644]
drivers/sensors/brcm/bbdpl2/bcm_gps_spi.c [new file with mode: 0644]

index c9829e2988282562910a5de68a16e099d1a0255e..2e1cc096fdd19fda0ef0d88a1da4ca5cdc966aa6 100644 (file)
@@ -14,8 +14,30 @@ config SENSORS_SSP_BBD
 
 choice
        depends on SENSORS_SSP_BBD
-       prompt "Choose target patch"
-       default SENSORHUB_TM2
+       prompt "Choose BBDPL Version"
+       default SENSORS_SSP_BBD_VER2
+
+config SENSORS_SSP_BBD_VER1
+       bool "bbdpl1"
+       depends on SPI
+       help
+         Sensorhub bbdpl version 1 for TM2(e).
+
+config SENSORS_SSP_BBD_VER2
+       bool "bbdpl2"
+       depends on SPI
+       help
+         Sensorhub bbdpl version 2 for TM2(e).
+endchoice
+
+if SENSORS_SSP_BBD_VER2
+source "drivers/sensors/brcm/bbdpl2/Kconfig"
+endif
+
+choice
+    depends on SENSORS_SSP_BBD
+    prompt "Choose target patch"
+    default SENSORHUB_TM2
 
 config SENSORHUB_S333
        bool "BROADCOM_SENSORHUB_S333"
index 71843a1eebe9f35f407b7eb20f746e56edb77202..b965e2119334c62165bebeda7489795a178b7cad 100644 (file)
@@ -14,6 +14,7 @@ obj-$(CONFIG_SENSORS_SSP_TMG399X)     += factory/light_tmg399x.o factory/prox_tmg399
 obj-$(CONFIG_SENSORS_SSP_BMP182)       += factory/pressure_bmp182.o
 obj-$(CONFIG_SENSORS_SSP_MOBEAM)       += factory/barcode_emul_tmg3992.o
 obj-$(CONFIG_SENSORS_SSP_SENSORHUB)    += ssp_sensorhub.o ssp_misc.o
-obj-$(CONFIG_SENSORS_SSP_BBD)           += ssp_bbd.o factory/mcu_bcm4773.o \
-                                               bbdpl1/
+obj-$(CONFIG_SENSORS_SSP_BBD)          += ssp_bbd.o factory/mcu_bcm4773.o
+obj-$(CONFIG_SENSORS_SSP_BBD_VER1)     += bbdpl1/
+obj-$(CONFIG_SENSORS_SSP_BBD_VER2)     += bbdpl2/
 obj-$(CONFIG_GPS_BCM47531)             += gps/sec_gps_bcm47531.o
diff --git a/drivers/sensors/brcm/bbdpl2/Kconfig b/drivers/sensors/brcm/bbdpl2/Kconfig
new file mode 100644 (file)
index 0000000..5697dde
--- /dev/null
@@ -0,0 +1,28 @@
+choice
+    depends on SENSORS_SSP_BBD
+    prompt "Choose serial connection with chip"
+    default BCM_GPS_SPI_DRIVER
+
+config BCM_GPS_TTY_DRIVER
+    bool  "TTY"
+    depends on TTY
+    help
+        Support for BRCM GPS UART driver.
+
+config BCM_GPS_I2C_DRIVER
+    bool "I2C"
+    depends on I2C
+    help
+        Support for BRCM GPS I2C driver.
+
+config BCM_GPS_SPI_DRIVER
+    bool "SPI"
+    depends on SPI
+    help
+        Support for BRCM GPS SPI driver.
+
+endchoice
+
+
+
+
diff --git a/drivers/sensors/brcm/bbdpl2/Makefile b/drivers/sensors/brcm/bbdpl2/Makefile
new file mode 100644 (file)
index 0000000..feeb3f5
--- /dev/null
@@ -0,0 +1,4 @@
+obj-$(CONFIG_BCM_GPS_TTY_DRIVER) += bcm_gps_tty.o
+obj-$(CONFIG_BCM_GPS_I2C_DRIVER) += bcm_gps_i2c.o
+obj-$(CONFIG_BCM_GPS_SPI_DRIVER) += bcm_gps_spi.o
+obj-$(CONFIG_SENSORS_SSP_BBD) += bbd_rpc_lh.o bbd.o
diff --git a/drivers/sensors/brcm/bbdpl2/bbd.c b/drivers/sensors/brcm/bbdpl2/bbd.c
new file mode 100644 (file)
index 0000000..ee98fb7
--- /dev/null
@@ -0,0 +1,997 @@
+/*
+ * Copyright 2014 Broadcom Corporation
+ *
+ * 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 (the "GPL").
+ *
+ * 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.
+ *
+ * A copy of the GPL is available at
+ * http://www.broadcom.com/licenses/GPLv2.php, or by writing to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * The BBD (Broadcom Bridge Driver)
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/cdev.h>
+#include <linux/sched.h>
+#include <linux/poll.h>
+#include <linux/wait.h>
+#include <linux/delay.h>
+#include <linux/circ_buf.h>
+#include <linux/fs.h>
+#include <linux/wakelock.h>
+#include <linux/suspend.h>
+#include <linux/notifier.h>
+#include <linux/of.h>
+#include "bbd.h"
+
+/* Needs because SSP is tightly coupled with SPI */
+#ifdef CONFIG_SENSORS_SSP
+#include <linux/spi/spi.h>
+
+extern struct spi_driver *pssp_driver;
+extern bool ssp_dbg;
+extern bool ssp_pkt_dbg;
+
+static struct spi_device dummy_spi = {
+       .dev = {
+               .init_name = "dummy",
+       },
+};
+#endif
+
+#ifdef CONFIG_BCM_GPS_SPI_DRIVER
+extern bool ssi_dbg;
+#endif
+
+void bbd_log_hex(const char*, const unsigned char*, unsigned long);
+
+//--------------------------------------------------------------
+//
+//               BBD device struct
+//
+//--------------------------------------------------------------
+#define BBD_BUFF_SIZE (PAGE_SIZE*2)
+struct bbd_cdev_priv {
+       const char* name;
+       struct cdev dev;                        /* char device */
+       bool busy;
+       struct circ_buf read_buf;               /* LHD reads from BBD */
+       struct mutex lock;                      /* Lock for read_buf */
+       char _read_buf[BBD_BUFF_SIZE];          /* LHD reads from BBD */
+       char write_buf[BBD_BUFF_SIZE];          /* LHD writes into BBD */
+       wait_queue_head_t poll_wait;            /* for poll */
+};
+
+struct bbd_device {
+       struct kobject *kobj;                   /* for sysfs register */
+       struct class *class;                    /* for device_create */
+
+       struct bbd_cdev_priv priv[BBD_DEVICE_INDEX];/* individual structures */
+       struct wake_lock bbd_wake_lock;
+       bool db;                                /* debug flag */
+
+       void *ssp_priv;                         /* private data pointer */
+       bbd_callbacks *ssp_cb;                  /* callbacks for SSP */
+
+};
+
+/*
+ * Character device names of BBD
+ */
+static const char *bbd_dev_name[BBD_DEVICE_INDEX] = {
+       "bbd_shmd",
+       "bbd_sensor",
+       "bbd_control",
+       "bbd_patch",
+       /* "bbd_ssi_spi_debug" */
+};
+
+//--------------------------------------------------------------
+//
+//               Globals
+//
+//--------------------------------------------------------------
+/*
+ * The global BBD device which has all necessary information.
+ * It's not beautiful but useful when we debug by Trace32.
+ */
+static struct bbd_device bbd;
+/*
+ * Embedded patch file provided as /dev/bbd_patch
+ */
+static unsigned char bbd_patch[] =
+{
+#if defined(CONFIG_SENSORHUB_TM2)
+#include "../platform/bbd_tm2_patch_file.h"
+#elif defined(CONFIG_SENSORHUB_S333)
+#include "../platform/bbd_s333_patch_file.h"
+#endif
+};
+
+/* Function to push read data into any bbd device's read buf */
+ssize_t bbd_on_read(unsigned int minor, const unsigned char *buf, size_t size);
+
+#ifdef DEBUG_1HZ_STAT
+
+static const char *bbd_stat_name[STAT_MAX] = {
+               "tx@lhd",
+               "tx@ssp",
+               "tx@rpc",
+               "tx@tl",
+               "tx@ssi",
+               "rx@ssi",
+               "rx@tl",
+               "rx@rpc",
+               "rx@ssp",
+               "rx@lhd"
+};
+
+struct bbd_stat stat1hz;
+
+//--------------------------------------------------------------
+//
+//               bbd 1hz statistics Functions
+//
+//--------------------------------------------------------------
+static void bbd_init_stat(void)
+{
+       memset(&stat1hz, 0, sizeof(stat1hz));
+
+       stat1hz.min_rx_lat = (u64)-1;
+       stat1hz.min_rx_dur = (u64)-1;
+       stat1hz.workq = create_singlethread_workqueue("BBD_1HZ_TICK");
+}
+
+static void bbd_exit_stat(void)
+{
+       bbd_disable_stat();
+       if (stat1hz.workq) {
+               flush_workqueue(stat1hz.workq);
+               destroy_workqueue(stat1hz.workq);
+               stat1hz.workq = 0;
+       }
+}
+
+static void bbd_report_stat(struct work_struct *work)
+{
+       char buf[512];
+       char *p = buf;
+       int i;
+
+       p += sprintf(p, "BBD:");
+       for (i = 0; i < STAT_MAX; i++)
+               p += sprintf(p, " %s=%llu", bbd_stat_name[i], stat1hz.stat[i]);
+       p += sprintf(p, " rxlat_min=%llu rxlat_max=%llu",
+               stat1hz.min_rx_lat, stat1hz.max_rx_lat);
+       p += sprintf(p, " rxdur_min=%llu rxdur_max=%llu",
+               stat1hz.min_rx_dur, stat1hz.max_rx_dur);
+
+       /* report only in case we had SSI traffic */
+       if (stat1hz.stat[STAT_TX_SSI] || stat1hz.stat[STAT_RX_SSI])
+               bbd_on_read(BBD_MINOR_CONTROL, buf, strlen(buf)+1);
+
+       for (i = 0; i < STAT_MAX; i++)
+               stat1hz.stat[i] = 0;
+
+       stat1hz.min_rx_lat = (u64)-1;
+       stat1hz.min_rx_dur = (u64)-1;
+       stat1hz.max_rx_lat = 0;
+       stat1hz.max_rx_dur = 0;
+}
+
+static void bbd_stat_timer_func(unsigned long p)
+{
+       if (stat1hz.workq)
+               queue_work(stat1hz.workq, &stat1hz.work);
+       mod_timer(&stat1hz.timer, jiffies + HZ);
+}
+
+void bbd_update_stat(int idx, unsigned int count)
+{
+       stat1hz.stat[idx] += count;
+}
+
+void bbd_enable_stat(void)
+{
+       if (stat1hz.enabled) {
+               printk("%s() 1HZ stat already enable. skipping.\n", __func__);
+               return;
+       }
+
+       INIT_WORK(&stat1hz.work, bbd_report_stat);
+       setup_timer(&stat1hz.timer, bbd_stat_timer_func, 0);
+       mod_timer(&stat1hz.timer, jiffies + HZ);
+       stat1hz.enabled = true;
+}
+
+void bbd_disable_stat(void)
+{
+       if (!stat1hz.enabled) {
+               printk("%s() 1HZ stat already disabled. skipping.\n", __func__);
+               return;
+       }
+       del_timer_sync(&stat1hz.timer);
+       cancel_work_sync(&stat1hz.work);
+       stat1hz.enabled = false;
+}
+#endif /* DEBUG_1HZ_STAT */
+//--------------------------------------------------------------
+//
+//               SHMD Interface Functions
+//
+//--------------------------------------------------------------
+
+/**
+ * bbd_register - Interface function called from SHMD to register itself to BBD
+ *
+ * @priv: SHMD's private data provided back to SHMD as callback argument
+ * @cb: SHMD's functions to be called
+ */
+void bbd_register(void *priv, bbd_callbacks *cb)
+{
+       bbd.ssp_priv = priv;
+       bbd.ssp_cb = cb;
+}
+EXPORT_SYMBOL(bbd_register);
+
+/**
+ * bbd_send_packet - Interface function called from SHMD to send sensor packet.
+ *
+ *     The sensor packet is pushed into /dev/bbd_sensor to be read by gpsd/lhd.
+ *     gpsd/lhd wrap the packet into RPC and sends to chip directly.
+ *
+ * @buf: buffer containing sensor packet
+ * @size: size of sensor packet
+ * @return: pushed data length = success
+ */
+struct sensor_pkt {
+       unsigned short size;
+       unsigned char buf[1022];        /*We assume max SSP packet less than 1KB */
+}__attribute__((__packed__)) ss_pkt;
+
+ssize_t bbd_send_packet(unsigned char *buf, size_t size)
+{
+       memset(&ss_pkt, 0, sizeof(ss_pkt));
+       ss_pkt.size = (unsigned short)size;
+       memcpy(ss_pkt.buf, buf, size);
+
+#ifdef DEBUG_1HZ_STAT
+       bbd_update_stat(STAT_TX_SSP, size);
+#endif
+       return bbd_on_read(BBD_MINOR_SENSOR, (unsigned char*)&ss_pkt, size+2); /* +2 for pkt.size */
+}
+EXPORT_SYMBOL(bbd_send_packet);
+
+
+/**
+ * bbd_pull_packet - Interface function called from SHMD to read sensor packet.
+ *
+ *     Read packet consists of sensor packet from gpsd/lhd and from BBD.
+ *
+ * @buf: buffer to receive packet
+ * @size:
+ * @timeout_ms: if specified, this function waits for sensor packet during given time
+ *
+ * @return: popped data length = success
+ */
+ssize_t bbd_pull_packet(unsigned char *buf, size_t size, unsigned int timeout_ms)
+{
+       struct circ_buf *circ = &bbd.priv[BBD_MINOR_SHMD].read_buf;
+       size_t rd_size = 0;
+
+       WARN_ON(!buf);
+       WARN_ON(!size);
+
+       if (timeout_ms) {
+               int ret = wait_event_interruptible_timeout(
+                                       bbd.priv[BBD_MINOR_SHMD].poll_wait,
+                                       circ->head != circ->tail,
+                                       msecs_to_jiffies(timeout_ms));
+               if (!ret)
+                       return -ETIMEDOUT;
+       }
+
+       mutex_lock(&bbd.priv[BBD_MINOR_SHMD].lock);
+
+       /* Copy from circ buffer to linear buffer
+        * Because SHMD's buffer is linear, we may require 2 copies
+        * from [tail..end] and [start..head]
+        */
+       do {
+               size_t cnt_to_end = CIRC_CNT_TO_END(circ->head, circ->tail, BBD_BUFF_SIZE);
+               size_t copied = min(cnt_to_end, size);
+
+               memcpy(buf + rd_size, (void*) circ->buf + circ->tail, copied);
+               size -= copied;
+               rd_size += copied;
+               circ->tail = (circ->tail + copied) & (BBD_BUFF_SIZE -1);
+
+       } while (size>0 && CIRC_CNT(circ->head, circ->tail, BBD_BUFF_SIZE));
+
+       mutex_unlock(&bbd.priv[BBD_MINOR_SHMD].lock);
+
+       return rd_size;
+}
+EXPORT_SYMBOL(bbd_pull_packet);
+
+/**
+ * bbd_mcu_reset - Interface function called from SHMD to reset chip
+ *
+ * BBD pushes reset request into /dev/bbd_control and actual reset is
+ * done by gpsd/lhd when it reads the request
+ *
+ * @return: 0 = success, -1 = failure
+ */
+int bbd_mcu_reset(void)
+{
+       pr_info("reset request from sensor hub\n");
+       return bbd_on_read(BBD_MINOR_CONTROL, BBD_CTRL_RESET_REQ,
+               strlen(BBD_CTRL_RESET_REQ)+1);
+}
+EXPORT_SYMBOL(bbd_mcu_reset);
+
+
+
+//--------------------------------------------------------------
+//
+//               BBD device struct
+//
+//--------------------------------------------------------------
+
+/**
+ * bbd_control - Handles command string from lhd
+ *
+ *
+ */
+ssize_t bbd_control(const char *buf, ssize_t len)
+{
+       if (strstr(buf, ESW_CTRL_READY)) {
+
+               if (bbd.ssp_cb && bbd.ssp_cb->on_mcu_ready)
+                       bbd.ssp_cb->on_mcu_ready(bbd.ssp_priv, true);
+
+       } else if (strstr(buf, ESW_CTRL_NOTREADY)) {
+               struct circ_buf *circ = &bbd.priv[BBD_MINOR_SENSOR].read_buf;
+               circ->head = circ->tail = 0;
+               if (bbd.ssp_cb && bbd.ssp_cb->on_mcu_ready)
+                       bbd.ssp_cb->on_mcu_ready(bbd.ssp_priv, false);
+       } else if (strstr(buf, ESW_CTRL_CRASHED)) {
+               struct circ_buf *circ = &bbd.priv[BBD_MINOR_SENSOR].read_buf;
+               circ->head = circ->tail = 0;
+
+               if (bbd.ssp_cb && bbd.ssp_cb->on_mcu_ready)
+                       bbd.ssp_cb->on_mcu_ready(bbd.ssp_priv, false);
+
+               if (bbd.ssp_cb && bbd.ssp_cb->on_control)
+                       bbd.ssp_cb->on_control(bbd.ssp_priv, buf);
+       } else if (strstr(buf, BBD_CTRL_DEBUG_OFF)) {
+               bbd.db = false;
+#ifdef CONFIG_SENSORS_SSP
+       } else if (strstr(buf, SSP_DEBUG_ON)) {
+               ssp_dbg = true;
+               ssp_pkt_dbg = true;
+       } else if (strstr(buf, SSP_DEBUG_OFF)) {
+               ssp_dbg = false;
+               ssp_pkt_dbg = false;
+#endif
+#ifdef CONFIG_BCM_GPS_SPI_DRIVER
+       } else if (strstr(buf, SSI_DEBUG_ON)) {
+               ssi_dbg = true;
+       } else if (strstr(buf, SSI_DEBUG_OFF)) {
+               ssi_dbg = false;
+#endif
+       } else if (bbd.ssp_cb && bbd.ssp_cb->on_control) {
+               /* Tell SHMD about the unknown control string */
+               bbd.ssp_cb->on_control(bbd.ssp_priv, buf);
+       }
+
+       return len;
+}
+
+
+
+//--------------------------------------------------------------
+//
+//               BBD Common File Functions
+//
+//--------------------------------------------------------------
+
+/**
+ * bbd_common_open - Common open function for BBD devices
+ *
+ */
+int bbd_common_open(struct inode *inode, struct file *filp)
+{
+       unsigned int minor = iminor(inode);
+       struct circ_buf *circ = &bbd.priv[minor].read_buf;
+
+       if (minor >= BBD_DEVICE_INDEX)
+               return -ENODEV;
+
+       pr_debug("%s", bbd.priv[minor].name);
+
+       if (bbd.priv[minor].busy && minor!=BBD_MINOR_CONTROL)
+               return -EBUSY;
+
+       bbd.priv[minor].busy = true;
+
+       /* Reset circ buffer */
+       circ->head = circ->tail = 0;
+
+       filp->private_data = &bbd;
+
+       return 0;
+}
+
+/**
+ * bbd_common_release - Common release function for BBD devices
+ */
+static int bbd_common_release(struct inode *inode, struct file *filp)
+{
+       unsigned int minor = iminor(inode);
+
+
+       BUG_ON(minor >= BBD_DEVICE_INDEX);
+       pr_debug("%s", bbd.priv[minor].name);
+
+       bbd.priv[minor].busy = false;
+
+       return 0;
+}
+
+/**
+ * bbd_common_read - Common read function for BBD devices
+ *
+ * lhd reads from BBD devices via this function
+ *
+ */
+static ssize_t bbd_common_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos)
+{
+       unsigned int minor = iminor(filp->f_path.dentry->d_inode);
+       struct circ_buf *circ = &bbd.priv[minor].read_buf;
+       size_t rd_size=0;
+
+       BUG_ON(minor >= BBD_DEVICE_INDEX);
+
+       mutex_lock(&bbd.priv[minor].lock);
+
+       /* Copy from circ buffer to lhd
+        * Because lhd's buffer is linear, we may require 2 copies
+        * from [tail..end] and [end..head]
+        */
+       do {
+               size_t cnt_to_end = CIRC_CNT_TO_END(circ->head, circ->tail, BBD_BUFF_SIZE);
+               size_t copied = min(cnt_to_end, size);
+
+               WARN_ON(copy_to_user(buf + rd_size, (void*) circ->buf + circ->tail, copied));
+               size -= copied;
+               rd_size += copied;
+               circ->tail = (circ->tail + copied) & (BBD_BUFF_SIZE -1);
+
+       } while (size>0 && CIRC_CNT(circ->head, circ->tail, BBD_BUFF_SIZE));
+
+       mutex_unlock(&bbd.priv[minor].lock);
+
+       bbd_log_hex(bbd_dev_name[minor], buf, rd_size);
+
+#ifdef DEBUG_1HZ_STAT
+       bbd_update_stat(STAT_RX_LHD, rd_size);
+#endif
+       return rd_size;
+}
+
+/**
+ * bbd_common_write - Common write function for BBD devices *
+ * lhd writes to BBD devices via this function
+ */
+static ssize_t bbd_common_write(struct file *filp, const char __user *buf,
+               size_t size, loff_t *ppos)
+{
+       unsigned int minor = iminor(filp->f_path.dentry->d_inode);
+
+       BUG_ON(size >= BBD_BUFF_SIZE);
+
+       WARN_ON(copy_from_user(bbd.priv[minor].write_buf, buf, size));
+
+#ifdef DEBUG_1HZ_STAT
+       bbd_update_stat(STAT_TX_LHD, size);
+#endif
+       return size;
+}
+
+/**
+ * bbd_common_poll - Common poll function for BBD devices
+ *
+ */
+static unsigned int bbd_common_poll(struct file *filp, poll_table *wait)
+{
+       unsigned int minor = iminor(filp->f_path.dentry->d_inode);
+       struct circ_buf *circ = &bbd.priv[minor].read_buf;
+       unsigned int mask = 0;
+
+       BUG_ON(minor >= BBD_DEVICE_INDEX);
+
+       poll_wait(filp, &bbd.priv[minor].poll_wait, wait);
+
+       if (CIRC_CNT(circ->head, circ->tail, BBD_BUFF_SIZE))
+               mask |= POLLIN;
+
+       return mask;
+}
+
+//--------------------------------------------------------------
+//
+//               BBD Device Specific File Functions
+//
+//--------------------------------------------------------------
+
+/**
+ * bbd_sensor_write - BBD's RPC calls this function to send sensor packet
+ *
+ * @buf: contains sensor packet coming from gpsd/lhd
+ *
+ */
+ssize_t bbd_sensor_write(const char *buf, size_t size)
+{
+       /* Copies into /dev/bbd_shmd. If SHMD was sleeping in poll_wait,
+        * bbd_on_read() wakes it up also
+        */
+       bbd_on_read(BBD_MINOR_SHMD, buf, size);
+
+#ifdef DEBUG_1HZ_STAT
+       bbd_update_stat(STAT_RX_SSP, size);
+#endif
+       /* OK. Now call pre-registered SHMD callbacks */
+       if (bbd.ssp_cb->on_packet)
+               bbd.ssp_cb->on_packet(bbd.ssp_priv, bbd.priv[BBD_MINOR_SHMD].write_buf, size);
+       else if (bbd.ssp_cb->on_packet_alarm)
+               bbd.ssp_cb->on_packet_alarm(bbd.ssp_priv);
+       else
+               pr_err("%s no SSP on_packet callback registered. "
+                               "Dropped %u bytes\n", __func__, (unsigned int)size);
+
+       return size;
+}
+
+/**
+ * bbd_control_write - Write function for BBD control (/dev/bbd_control)
+ *
+ *  Receives control string from lhd and handles it
+ *
+ */
+ssize_t bbd_control_write(struct file *filp, const char __user *buf, size_t size, loff_t *ppos)
+{
+       unsigned int minor = iminor(filp->f_path.dentry->d_inode);
+
+       /* get command string first */
+       ssize_t len = bbd_common_write(filp, buf, size, ppos);
+       if (len <= 0)
+               return len;
+
+       /* Process received command string */
+       return bbd_control(bbd.priv[minor].write_buf, len);
+}
+
+ssize_t bbd_patch_read( struct file *filp, char __user *buf, size_t size, loff_t *ppos)
+{
+        ssize_t rd_size = size;
+        size_t  offset = filp->f_pos;
+
+       if (offset >= sizeof(bbd_patch)) {       /* signal EOF */
+               *ppos = 0;
+               return 0;
+       }
+       if (offset+size > sizeof(bbd_patch))
+               rd_size = sizeof(bbd_patch) - offset;
+       if (copy_to_user(buf, bbd_patch + offset, rd_size))
+               rd_size = -EFAULT;
+       else
+               *ppos = filp->f_pos + rd_size;
+
+        return rd_size;
+}
+
+
+//--------------------------------------------------------------
+//
+//               Sysfs
+//
+//--------------------------------------------------------------
+static ssize_t store_sysfs_bbd_control(struct device *dev, struct device_attribute *attr, const char *buf, size_t len)
+{
+       bbd_control(buf, strlen(buf)+1);
+       return len;
+}
+
+ssize_t bbd_request_mcu(bool on);
+static ssize_t show_sysfs_bbd_pl(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       return 0;
+}
+
+static DEVICE_ATTR(bbd,     0220, NULL,                store_sysfs_bbd_control);
+static DEVICE_ATTR(pl,      0440, show_sysfs_bbd_pl,       NULL);
+
+static struct attribute *bbd_attributes[] = {
+       &dev_attr_bbd.attr,
+       &dev_attr_pl.attr,
+       NULL
+};
+
+static const struct attribute_group bbd_group = {
+       .attrs = bbd_attributes,
+};
+
+
+//--------------------------------------------------------------
+//
+//               Misc Functions
+//
+//--------------------------------------------------------------
+void bbd_log_hex(const char*          pIntroduction,
+               const unsigned char* pData,
+               unsigned long        ulDataLen)
+{
+       const unsigned char* pDataEnd = pData + ulDataLen;
+
+       if (likely(!bbd.db))
+               return;
+       if (!pIntroduction) pIntroduction = "...unknown...";
+
+       while (pData < pDataEnd)
+       {
+               char buf[128];
+               size_t bufsize = sizeof(buf) - 3;
+               size_t lineLen = pDataEnd - pData;
+               size_t perLineCount = lineLen;
+               if (lineLen > 32) {
+                       lineLen = 32;
+                       perLineCount = lineLen;
+               }
+
+               snprintf(buf, bufsize, "%s [%u] { ", pIntroduction,
+                               (unsigned int)lineLen);
+
+               for (; perLineCount > 0; ++pData, --perLineCount)
+               {
+                       size_t len = strlen(buf);
+                       snprintf(buf+len, bufsize - len, "%02X ", *pData);
+               }
+               printk(KERN_INFO"%s}\n", buf);
+       }
+}
+
+/**
+ *
+ * bbd_on_read - Push data into read buffer of specified char device.
+ *   if minor is bbd_sensor
+ *
+ * @buf: linear buffer
+ */
+ssize_t bbd_on_read(unsigned int minor, const unsigned char *buf, size_t size)
+{
+       struct circ_buf *circ = &bbd.priv[minor].read_buf;
+       size_t wr_size = 0;
+
+       bbd_log_hex(bbd_dev_name[minor], buf, size);
+
+       mutex_lock(&bbd.priv[minor].lock);
+
+       /* If there's not enough speace, drop it but try waking up reader */
+       if (CIRC_SPACE(circ->head, circ->tail, BBD_BUFF_SIZE)<size) {
+               pr_err("%s read buffer full. Dropping %u bytes\n",
+                               bbd_dev_name[minor], (unsigned int)size);
+               goto skip;
+       }
+
+       /* Copy into circ buffer from linear buffer
+        * We may require 2 copies from [head..end] and [start..head]
+        */
+       do {
+               size_t space_to_end = CIRC_SPACE_TO_END(circ->head, circ->tail, BBD_BUFF_SIZE);
+               size_t copied = min(space_to_end, size);
+
+               memcpy((void*) circ->buf + circ->head, buf + wr_size, copied);
+               size -= copied;
+               wr_size += copied;
+               circ->head = (circ->head + copied) & (BBD_BUFF_SIZE -1);
+
+       } while (size>0 && CIRC_SPACE(circ->head, circ->tail, BBD_BUFF_SIZE));
+skip:
+       mutex_unlock(&bbd.priv[minor].lock);
+
+       /* Wake up reader */
+       wake_up(&bbd.priv[minor].poll_wait);
+
+
+       return wr_size;
+}
+
+ssize_t bbd_request_mcu(bool on)
+{
+       printk("%s(%s) called", __func__, (on)?"On":"Off");
+       if (on)
+               return bbd_on_read(BBD_MINOR_CONTROL, GPSD_SENSOR_ON, strlen(GPSD_SENSOR_ON)+1);
+       else {
+               bbd.ssp_cb->on_mcu_ready(bbd.ssp_priv, false);
+               return bbd_on_read(BBD_MINOR_CONTROL, GPSD_SENSOR_OFF, strlen(GPSD_SENSOR_OFF)+1);
+       }
+}
+EXPORT_SYMBOL(bbd_request_mcu);
+
+//--------------------------------------------------------------
+//
+//               PM operation
+//
+//--------------------------------------------------------------
+static int bbd_suspend(pm_message_t state)
+{
+#ifdef DEBUG_1HZ_STAT
+       bbd_disable_stat();
+#endif
+#ifdef CONFIG_SENSORS_SSP
+       /* Call SSP suspend */
+       if (pssp_driver->driver.pm && pssp_driver->driver.pm->suspend)
+               pssp_driver->driver.pm->suspend(&dummy_spi.dev);
+#endif
+       mdelay(20);
+
+     return 0;
+}
+
+static int bbd_resume(void)
+{
+#ifdef CONFIG_SENSORS_SSP
+       /* Call SSP resume */
+       if (pssp_driver->driver.pm && pssp_driver->driver.pm->suspend)
+               pssp_driver->driver.pm->resume(&dummy_spi.dev);
+#endif
+#ifdef DEBUG_1HZ_STAT
+       bbd_enable_stat();
+#endif
+       wake_lock_timeout(&bbd.bbd_wake_lock, HZ/2);
+
+       return 0;
+}
+
+static int bbd_notifier(struct notifier_block *nb, unsigned long event, void * data)
+{
+       pm_message_t state = {0};
+        switch (event) {
+               case PM_SUSPEND_PREPARE:
+                       printk("%s going to sleep", __func__);
+                       state.event = event;
+                       bbd_suspend(state);
+                       break;
+               case PM_POST_SUSPEND:
+                       printk("%s waking up", __func__);
+                       bbd_resume();
+                       break;
+       }
+       return NOTIFY_OK;
+}
+
+static struct notifier_block bbd_notifier_block = {
+        .notifier_call = bbd_notifier,
+};
+
+//--------------------------------------------------------------
+//
+//               BBD Device Init and Exit
+//
+//--------------------------------------------------------------
+
+
+static const struct file_operations bbd_fops[BBD_DEVICE_INDEX] = {
+        /* bbd shmd file operations */
+       {
+                .owner          =  THIS_MODULE,
+       },
+        /* bbd sensor file operations */
+        {
+                .owner          =  THIS_MODULE,
+                .open           =  bbd_common_open,
+                .release        =  bbd_common_release,
+                .read           =  bbd_common_read,
+                .write          =  NULL,
+                .poll           =  bbd_common_poll,
+        },
+       /* bbd control file operations */
+       {
+               .owner          =  THIS_MODULE,
+               .open           =  bbd_common_open,
+               .release        =  bbd_common_release,
+               .read           =  bbd_common_read,
+               .write          =  bbd_control_write,
+               .poll           =  bbd_common_poll,
+       },
+       /* bbd patch file operations */
+       {
+               .owner          =  THIS_MODULE,
+               .open           =  bbd_common_open,
+               .release        =  bbd_common_release,
+               .read           =  bbd_patch_read,
+               .write          =  NULL, /* /dev/bbd_patch is read-only */
+               .poll           =  NULL,
+       },
+};
+
+
+int bbd_init(struct device* dev)
+{
+       int minor, ret = -ENOMEM;
+       struct timespec ts1;
+       unsigned long start, elapsed;
+
+       ts1 = ktime_to_timespec(ktime_get_boottime());
+       start = ts1.tv_sec * 1000000000ULL + ts1.tv_nsec;
+
+
+       pr_err("BBD start: %s, %d\n", __func__, __LINE__);
+
+       /* Initialize BBD device */
+       memset(&bbd, 0, sizeof(bbd));
+       wake_lock_init(&bbd.bbd_wake_lock, WAKE_LOCK_SUSPEND, "bbd_wake_lock");
+
+       /* Create class which is required for device_create() */
+       bbd.class = class_create(THIS_MODULE, "bbd");
+       if (IS_ERR(bbd.class)) {
+               WARN("BBD:%s() failed to create class \"bbd\"", __func__);
+               goto exit;
+       }
+
+       /* Create BBD char devices */
+       for (minor=0; minor<BBD_DEVICE_INDEX; minor++) {
+               dev_t devno = MKDEV(BBD_DEVICE_MAJOR, minor);
+               struct cdev *cdev = &bbd.priv[minor].dev;
+               const char *name = bbd_dev_name[minor];
+               struct device *dev;
+
+               /* Init buf, waitqueue, mutex, etc. */
+               bbd.priv[minor].name = bbd_dev_name[minor];
+               bbd.priv[minor].read_buf.buf = bbd.priv[minor]._read_buf;
+
+               init_waitqueue_head(&bbd.priv[minor].poll_wait);
+               mutex_init(&bbd.priv[minor].lock);
+
+               /* Don't register /dev/bbd_shmd */
+               if (minor==BBD_MINOR_SHMD)
+                       continue;
+               /* Reserve char device number (a.k.a, major, minor) for this BBD device */
+               ret = register_chrdev_region(devno, 1, name);
+               if (ret) {
+                       pr_err("BBD:%s() failed to register_chrdev_region() "
+                                       "\"%s\", ret=%d", __func__, name, ret);
+                       goto free_class;
+               }
+
+               /* Register cdev which relates above device number with this BBD device */
+               cdev_init(cdev, &bbd_fops[minor]);
+               cdev->owner = THIS_MODULE;
+               cdev->ops = &bbd_fops[minor];
+               ret = cdev_add(cdev, devno, 1);
+               if (ret) {
+                       pr_err("BBD:%s()) failed to cdev_add() \"%s\", ret=%d",
+                                                       __func__, name, ret);
+                       unregister_chrdev_region(devno, 1);
+                       goto free_class;
+               }
+
+               /* Let it show in FS */
+               dev = device_create(bbd.class, NULL, devno, NULL, "%s", name);
+               if (IS_ERR_OR_NULL(dev)) {
+                       pr_err("BBD:%s() failed to device_create() "
+                               "\"%s\", ret=%d", __func__, name, ret);
+                       unregister_chrdev_region(devno, 1);
+                       cdev_del(&bbd.priv[minor].dev);
+                       goto free_class;
+               }
+
+               /* Done. Put success log and init BBD specific fields */
+               pr_debug("BBD:%s(%d,%d) registered /dev/%s\n",
+                             __func__, BBD_DEVICE_MAJOR,minor,name);
+
+       }
+
+       /* Register sysfs entry */
+       bbd.kobj = kobject_create_and_add("bbd", NULL);
+       BUG_ON(!bbd.kobj);
+       ret = sysfs_create_group(bbd.kobj, &bbd_group);
+       if (ret < 0) {
+               pr_err("%s failed to sysfs_create_group \"bbd\", ret = %d",
+                                                       __func__, ret);
+               goto free_kobj;
+       }
+
+
+       /* Register PM */
+       ret = register_pm_notifier(&bbd_notifier_block);
+       BUG_ON(ret);
+
+#ifdef CONFIG_SENSORS_SSP
+       /* Now, we can initialize SSP */
+       BUG_ON(device_register(&dummy_spi.dev));
+       {
+               struct spi_device *spi = to_spi_device(dev);
+               void *org_priv, *new_priv;
+
+               org_priv = spi_get_drvdata(spi);
+               pssp_driver->probe(spi);
+               new_priv = spi_get_drvdata(spi);
+               spi_set_drvdata(spi, org_priv);
+               spi_set_drvdata(&dummy_spi, new_priv);
+
+       }
+#endif
+       ts1 = ktime_to_timespec(ktime_get_boottime());
+       elapsed = (ts1.tv_sec * 1000000000ULL + ts1.tv_nsec) - start;
+       pr_debug("BBD:%s %lu nsec elapsed\n", __func__, elapsed);
+
+#ifdef DEBUG_1HZ_STAT
+       bbd_init_stat();
+#endif
+        return 0;
+
+free_kobj:
+       kobject_put(bbd.kobj);
+free_class:
+       while (--minor>BBD_MINOR_SHMD) {
+               dev_t devno = MKDEV(BBD_DEVICE_MAJOR, minor);
+               struct cdev *cdev = &bbd.priv[minor].dev;
+
+               device_destroy(bbd.class, devno);
+               cdev_del(cdev);
+               unregister_chrdev_region(devno, 1);
+       }
+       class_destroy(bbd.class);
+exit:
+       return ret;
+}
+
+static void __exit bbd_exit(void)
+{
+       int minor;
+
+#ifdef CONFIG_SENSORS_SSP
+       /* Shutdown SSP first*/
+       pssp_driver->shutdown(&dummy_spi);
+#endif
+
+       /* Remove sysfs entry */
+       sysfs_remove_group(bbd.kobj, &bbd_group);
+
+       /* Remove BBD char devices */
+       for (minor=BBD_MINOR_SENSOR; minor<BBD_DEVICE_INDEX; minor++) {
+               dev_t devno = MKDEV(BBD_DEVICE_MAJOR, minor);
+               struct cdev *cdev = &bbd.priv[minor].dev;
+               const char *name = bbd_dev_name[minor];
+
+               device_destroy(bbd.class, devno);
+               cdev_del(cdev);
+               unregister_chrdev_region(devno, 1);
+
+               pr_debug("%s(%d,%d) unregistered /dev/%s\n",
+                               __func__, BBD_DEVICE_MAJOR, minor, name);
+       }
+
+#ifdef DEBUG_1HZ_STAT
+       bbd_exit_stat();
+#endif
+       /* Remove class */
+       class_destroy(bbd.class);
+       /* Done. Put success log */
+}
+
+MODULE_AUTHOR("Broadcom");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/sensors/brcm/bbdpl2/bbd.h b/drivers/sensors/brcm/bbdpl2/bbd.h
new file mode 100644 (file)
index 0000000..c632390
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2015 Broadcom Corporation
+ *
+ * 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 (the "GPL").
+ *
+ * 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.
+ *  
+ * A copy of the GPL is available at 
+ * http://www.broadcom.com/licenses/GPLv2.php, or by writing to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * The BBD (Broadcom Bridge Driver)
+ */
+
+#ifndef __BBD_H__
+#define __BBD_H__
+
+
+#define BBD_DEVICE_MAJOR       239
+enum {
+       BBD_MINOR_SHMD      = 0,
+       BBD_MINOR_SENSOR    = 1,
+       BBD_MINOR_CONTROL   = 2,
+       BBD_MINOR_PATCH     = 3,
+       BBD_DEVICE_INDEX
+};
+
+#define BBD_MAX_DATA_SIZE       4096  /* max data size for transition  */
+
+#define BBD_CTRL_RESET_REQ     "BBD:RESET_REQ"
+#define ESW_CTRL_READY         "ESW:READY"
+#define ESW_CTRL_NOTREADY      "ESW:NOTREADY"
+#define ESW_CTRL_CRASHED       "ESW:CRASHED"
+#define BBD_CTRL_DEBUG_ON       "BBD:DEBUG=1"
+#define BBD_CTRL_DEBUG_OFF      "BBD:DEBUG=0"
+#define SSP_DEBUG_ON           "SSP:DEBUG=1"
+#define SSP_DEBUG_OFF          "SSP:DEBUG=0"
+#define SSI_DEBUG_ON           "SSI:DEBUG=1"
+#define SSI_DEBUG_OFF          "SSI:DEBUG=0"
+#define BBD_CTRL_SSI_PATCH_BEGIN       "SSI:PatchBegin"
+#define BBD_CTRL_SSI_PATCH_END         "SSI:PatchEnd"
+#define GPSD_SENSOR_ON         "GPSD:SENSOR_ON"
+#define GPSD_SENSOR_OFF                "GPSD:SENSOR_OFF"
+
+#ifdef DEBUG_1HZ_STAT
+
+enum {
+       STAT_TX_LHD = 0,
+       STAT_TX_SSP,
+       STAT_TX_RPC,
+       STAT_TX_TL,
+       STAT_TX_SSI,
+
+       STAT_RX_SSI,
+       STAT_RX_TL,
+       STAT_RX_RPC,
+       STAT_RX_SSP,
+       STAT_RX_LHD,
+
+       STAT_MAX
+};
+
+
+struct bbd_stat {
+       bool enabled;
+
+       u64 ts_irq;
+
+       u64 min_rx_lat; /* = (u64)-1 */
+       u64 min_rx_dur; /* = (u64)-1 */
+
+       u64 max_rx_lat; /* = 0 */
+       u64 max_rx_dur; /* = 0 */
+
+       volatile u64 stat[STAT_MAX];
+
+       struct timer_list timer;
+       struct work_struct work;
+       struct workqueue_struct *workq;
+};
+
+extern struct bbd_stat stat1hz;
+
+void bbd_update_stat(int index, unsigned int count);
+void bbd_enable_stat(void);
+void bbd_disable_stat(void);
+#endif
+
+
+/** callback for incoming data from 477x to senser hub driver **/
+typedef struct {
+       int (*on_packet)(void *ssh_data, const char *buf, size_t size);
+       int (*on_packet_alarm)(void *ssh_data);
+       int (*on_control)(void *ssh_data, const char *str_ctrl);
+       int (*on_mcu_ready)(void *ssh_data, bool ready);
+} bbd_callbacks;
+
+extern void    bbd_register(void* ext_data, bbd_callbacks *pcallbacks);
+extern ssize_t bbd_send_packet(unsigned char *buf, size_t size);
+extern ssize_t bbd_pull_packet(unsigned char *buf, size_t size, unsigned int timeout_ms);
+extern int     bbd_mcu_reset(void);
+extern int     bbd_init(struct device* dev);
+#endif /* __BBD_H__ */
diff --git a/drivers/sensors/brcm/bbdpl2/bbd_rpc_lh.c b/drivers/sensors/brcm/bbdpl2/bbd_rpc_lh.c
new file mode 100644 (file)
index 0000000..5a49fc3
--- /dev/null
@@ -0,0 +1,712 @@
+/*
+ * Copyright 2015 Broadcom Corporation
+ *
+ * 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 (the "GPL").
+ *
+ * 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.
+ *
+ * A copy of the GPL is available at
+ * http://www.broadcom.com/licenses/GPLv2.php, or by writing to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * Sensor RPC handler for 4773/4774
+ *
+ * tabstop = 8
+ */
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/bug.h>
+#include <linux/printk.h>
+#include "bbd.h"
+/* function to inject sensor data into SHMD */
+ssize_t bbd_sensor_write(const char *buf, unsigned int size);
+void bcm_on_packet_recieved(void *_priv, unsigned char *data, unsigned int size);
+
+/*
+ ******************* CUSTOMIZATION FILE ********************
+ * This file can be edited for your project, and you can customize
+ * your transport layer by changing some of the default value
+ * Note that you can also define these value from your makefile directly
+ */
+/* if defined to 1, the reliable channel will be enabled.
+ * If defined to 0, it will be disabled and compiled out
+ */
+#ifndef TLCUST_ENABLE_RELIABLE_PL
+#define TLCUST_ENABLE_RELIABLE_PL 1
+#endif
+/* Defines the max size of outgoing packet. That should match
+ * the max size of incoming packet from the remote TL
+ */
+#ifndef TLCUST_MAX_OUTGOING_PACKET_SIZE
+#define TLCUST_MAX_OUTGOING_PACKET_SIZE 2048
+#endif
+/* Defines the max size of incoming packet. That should match
+ * the max size of outgoing packet from the remote TL
+ */
+#ifndef TLCUST_MAX_INCOMING_PACKET_SIZE
+#define TLCUST_MAX_INCOMING_PACKET_SIZE 2048
+#endif
+/* Defines the number of millisecond before retrying a reliable
+ * packet when no acknowledgement is received.
+ */
+#ifndef TLCUST_RELIABLE_RETRY_TIMEOUT_MS
+#define TLCUST_RELIABLE_RETRY_TIMEOUT_MS 1000
+#endif
+/* Defines the number of retries before declaring a communication error */
+#ifndef TLCUST_RELIABLE_MAX_RETRY
+#define TLCUST_RELIABLE_MAX_RETRY 10
+#endif
+
+/* Defines the maximum number of reliable packets that can be in transit(MAX is 255) */
+#ifndef TLCUST_RELIABLE_MAX_PACKETS
+#define TLCUST_RELIABLE_MAX_PACKETS 150
+#endif
+/* public constants that are built from the customization file */
+#define MAX_OUTGOING_PACKET_SIZE  TLCUST_MAX_OUTGOING_PACKET_SIZE
+#define MAX_INCOMING_PACKET_SIZE  TLCUST_MAX_INCOMING_PACKET_SIZE
+#define RELIABLE_RETRY_TIMEOUT_MS TLCUST_RELIABLE_RETRY_TIMEOUT_MS
+#define RELIABLE_MAX_RETRY        TLCUST_RELIABLE_MAX_RETRY
+#define RELIABLE_MAX_PACKETS      TLCUST_RELIABLE_MAX_PACKETS
+#define MAX_HEADER_SIZE 14
+
+#define _DIM(x) ((unsigned int )(sizeof(x)/sizeof(*(x))))
+
+/*
+ * The following are used for the software flow control (UART)
+ */
+static const unsigned char XON_CHARACTER = 0x11;
+static const unsigned char XOFF_CHARACTER = 0x13;
+/*
+ * The following are used for the protocol.
+ */
+static const unsigned char ESCAPE_CHARACTER         = 0xB0;
+static const unsigned char SOP_CHARACTER            = 0x00;
+static const unsigned char EOP_CHARACTER            = 0x01;
+static const unsigned char ESCAPED_ESCAPE_CHARACTER = 0x03;
+static const unsigned char ESCAPED_XON_CHARACTER    = 0x04;
+static const unsigned char ESCAPED_XOFF_CHARACTER   = 0x05;
+/*
+ * The following are the bit field definition for the flags
+ */
+static const unsigned short FLAG_PACKET_ACK      = (1<<0); /* ACK of a received packet. Flag detail contains the ACK SeqId */
+static const unsigned short FLAG_RELIABLE_PACKET = (1<<1); /* Indicates that this is a reliable packet. Flag detail contains the remote reliable seqId */
+static const unsigned short FLAG_RELIABLE_ACK    = (1<<2); /* A reliable SeqId was Acked. Flag detail contains the acked reliable seqId */
+static const unsigned short FLAG_RELIABLE_NACK   = (1<<3); /* A reliable SeqId error was detected. Flag detail contains the last remote reliable seqId */
+
+static const unsigned short FLAG_MSG_LOST        = (1<<4); /* Remote PacketLayer detected lost packets (jumps in SeqId). The flag details contains the last remote seqId */
+static const unsigned short FLAG_MSG_GARBAGE     = (1<<5); /* Garbage bytes detected. The Flag details will contains the number of garbage bytes (capped to 255) */
+static const unsigned short FLAG_SIZE_EXTENDED   = (1<<6); /* Size of packet will have one byte extension (MSB), contained in the Flags detail */
+static const unsigned short FLAG_EXTENDED        = (1<<7); /* If set, then the flag details will contains a Byte representing the MSB of the 16bit flags */
+static const unsigned short FLAG_INTERNAL_PACKET = (1<<8); /* Packet in that message is internal, and should be processed by the TL */
+static const unsigned short FLAG_IGNORE_SEQID    = (1<<9); /* Remote side requested to ignore the seqId, i.e. error should not be accounted for in the stats */
+/* Enumeration of all the RPCs for the codec. DO NOT CHANGE THE ORDER,
+ * DELETE, or INSERT anything to keep backward compatibility.
+ */
+#define RPC_DEFINITION(klass, method) RPC_##klass##_##method
+enum
+{
+       RPC_DEFINITION(IRpcA, A)
+               ,RPC_DEFINITION(IRpcA, B)
+               ,RPC_DEFINITION(IRpcA, C)
+               ,RPC_DEFINITION(IRpcB, A)
+               ,RPC_DEFINITION(IRpcB, B)
+               ,RPC_DEFINITION(IRpcC, A)
+               ,RPC_DEFINITION(IRpcC, B)
+               ,RPC_DEFINITION(IRpcC, C)
+               ,RPC_DEFINITION(IRpcD, A)
+               ,RPC_DEFINITION(IRpcE, A)
+               ,RPC_DEFINITION(IRpcE, B)
+               ,RPC_DEFINITION(IRpcF, A)
+               ,RPC_DEFINITION(IRpcF, B)
+               ,RPC_DEFINITION(IRpcF, C)
+               ,RPC_DEFINITION(IRpcG, A)
+               ,RPC_DEFINITION(IRpcG, B)
+               ,RPC_DEFINITION(IRpcG, C)
+               ,RPC_DEFINITION(IRpcG, D)
+               ,RPC_DEFINITION(IRpcG, E)
+               ,RPC_DEFINITION(IRpcH, A)
+               ,RPC_DEFINITION(IRpcH, B)
+               ,RPC_DEFINITION(IRpcH, C)
+               ,RPC_DEFINITION(IRpcH, D)
+               ,RPC_DEFINITION(IRpcI, A)
+               ,RPC_DEFINITION(IRpcI, B)
+               ,RPC_DEFINITION(IRpcI, C)
+               ,RPC_DEFINITION(IRpcJ, A)
+               ,RPC_DEFINITION(IRpcJ, B)
+               ,RPC_DEFINITION(IRpcK, A)
+               ,RPC_DEFINITION(IRpcK, B)
+               ,RPC_DEFINITION(IRpcK, C)
+               ,RPC_DEFINITION(IRpcL, A)
+               ,RPC_DEFINITION(IRpcSensorRequest,  Data)
+               ,RPC_DEFINITION(IRpcSensorResponse, Data)
+};
+/* enumeration for the state */
+enum
+{
+       WAIT_FOR_ESC_SOP = 0
+               ,WAIT_FOR_SOP
+               ,WAIT_FOR_MESSAGE_COMPLETE
+               ,WAIT_FOR_EOP
+};
+
+typedef struct stTransportLayerStats
+{
+       unsigned int ulRxGarbageBytes;
+       unsigned int ulRxPacketLost;
+       unsigned int ulRemotePacketLost;   /* this is approximate as it is reported and the report could be lost. */
+       unsigned int ulRemoteGarbage;      /* this is approximate as it is reported and the report could be lost. */
+       unsigned int ulPacketSent;         /* number of normal packet sent */
+       unsigned int ulPacketReceived;
+       unsigned int ulAckReceived;
+       unsigned int ulReliablePacketSent;
+       unsigned int ulReliableRetransmit;
+       unsigned int ulReliablePacketReceived;
+       unsigned int ulMaxRetransmitCount;
+}stTransportLayerStats;
+static unsigned int m_uiParserState;
+static unsigned int m_uiRxLen;
+static unsigned int m_uiEscLen;
+static unsigned int m_ulByteCntSinceLastValidPacket;
+static unsigned char m_aucRxMessageBuf[MAX_INCOMING_PACKET_SIZE+MAX_HEADER_SIZE];
+static unsigned char m_aucRxEscapedBuf[(MAX_INCOMING_PACKET_SIZE+MAX_HEADER_SIZE+2)*2];
+static stTransportLayerStats m_otCurrentStats;
+static unsigned char m_ucDelayAckCount;
+
+static unsigned char m_ucLastRxSeqId;
+static unsigned char m_ucLastAckSeqId;
+static unsigned char m_ucReliableSeqId;
+static unsigned char m_ucReliableCrc;
+static unsigned short m_usReliableLen;
+static bool m_bOngoingSync;
+
+/**
+ * CRC table - from GlUtlCrc::ucCrcTable
+ *
+ */
+static const unsigned char crc_table [] =
+{
+       0x00,0x4d,0x9a,0xd7,0x79,0x34,0xe3,0xae,0xf2,0xbf,0x68,0x25,0x8b,0xc6,0x11,0x5c,
+       0xa9,0xe4,0x33,0x7e,0xd0,0x9d,0x4a,0x07,0x5b,0x16,0xc1,0x8c,0x22,0x6f,0xb8,0xf5,
+       0x1f,0x52,0x85,0xc8,0x66,0x2b,0xfc,0xb1,0xed,0xa0,0x77,0x3a,0x94,0xd9,0x0e,0x43,
+       0xb6,0xfb,0x2c,0x61,0xcf,0x82,0x55,0x18,0x44,0x09,0xde,0x93,0x3d,0x70,0xa7,0xea,
+       0x3e,0x73,0xa4,0xe9,0x47,0x0a,0xdd,0x90,0xcc,0x81,0x56,0x1b,0xb5,0xf8,0x2f,0x62,
+       0x97,0xda,0x0d,0x40,0xee,0xa3,0x74,0x39,0x65,0x28,0xff,0xb2,0x1c,0x51,0x86,0xcb,
+       0x21,0x6c,0xbb,0xf6,0x58,0x15,0xc2,0x8f,0xd3,0x9e,0x49,0x04,0xaa,0xe7,0x30,0x7d,
+       0x88,0xc5,0x12,0x5f,0xf1,0xbc,0x6b,0x26,0x7a,0x37,0xe0,0xad,0x03,0x4e,0x99,0xd4,
+       0x7c,0x31,0xe6,0xab,0x05,0x48,0x9f,0xd2,0x8e,0xc3,0x14,0x59,0xf7,0xba,0x6d,0x20,
+       0xd5,0x98,0x4f,0x02,0xac,0xe1,0x36,0x7b,0x27,0x6a,0xbd,0xf0,0x5e,0x13,0xc4,0x89,
+       0x63,0x2e,0xf9,0xb4,0x1a,0x57,0x80,0xcd,0x91,0xdc,0x0b,0x46,0xe8,0xa5,0x72,0x3f,
+       0xca,0x87,0x50,0x1d,0xb3,0xfe,0x29,0x64,0x38,0x75,0xa2,0xef,0x41,0x0c,0xdb,0x96,
+       0x42,0x0f,0xd8,0x95,0x3b,0x76,0xa1,0xec,0xb0,0xfd,0x2a,0x67,0xc9,0x84,0x53,0x1e,
+       0xeb,0xa6,0x71,0x3c,0x92,0xdf,0x08,0x45,0x19,0x54,0x83,0xce,0x60,0x2d,0xfa,0xb7,
+       0x5d,0x10,0xc7,0x8a,0x24,0x69,0xbe,0xf3,0xaf,0xe2,0x35,0x78,0xd6,0x9b,0x4c,0x01,
+       0xf4,0xb9,0x6e,0x23,0x8d,0xc0,0x17,0x5a,0x06,0x4b,0x9c,0xd1,0x7f,0x32,0xe5,0xa8
+};
+/**
+ * crc_calc from GlUtlCrc::GlUtlCrcCalc
+ * update with new data byte and get result
+ */
+static inline unsigned char crc_calc(unsigned char *m_ucCrcState, unsigned char ucData)
+{
+       *m_ucCrcState = crc_table[*m_ucCrcState ^ ucData];
+       return *m_ucCrcState;
+}
+
+/**
+ * crc_calc_many - from GlUtlCrc::GlUtlCrcCalc
+ *
+ */
+static unsigned char crc_calc_many(unsigned char *m_ucCrcState, const unsigned char *pucData, unsigned short usLen)
+{
+       while (usLen--)
+       {
+               *m_ucCrcState =  crc_table[*m_ucCrcState ^ (*pucData++)];
+       }
+       return *m_ucCrcState;
+}
+
+/*
+ * BbdBridge_OnRpcReceived - copied from RpcEngine::OnRpcReceived
+ *
+ */
+static int BbdBridge_OnRpcReceived(unsigned short usRpcId, unsigned char *pRpcPayload, unsigned short usRpcLen)
+{
+       if (usRpcId == RPC_DEFINITION(IRpcSensorResponse, Data))
+       {
+               /* Read 2 byte size */
+               ssize_t result;
+               unsigned short size = *pRpcPayload++;
+               size |= *pRpcPayload++ <<8;
+               result = bbd_sensor_write(pRpcPayload, size);
+               WARN_ON( size != usRpcLen-2);
+               WARN_ON((short) result != size);
+               return 1;
+       }
+       return 0;
+}
+/*
+ * BbdBridge_CheckPacketSanity - copied from RpcEngine::OnPacketReceived
+ *
+ */
+static bool BbdBridge_CheckPacketSanity(unsigned char *pucData, unsigned short usSize)
+{
+       long lSize = (long) usSize;
+       while (lSize > 0)
+       {
+               unsigned short usRpcId = *pucData++;
+               unsigned short usRpcLen;
+               lSize--;
+               if (usRpcId&0x80)
+               {
+                       usRpcId &= ~0x80;
+                       usRpcId <<= 8;
+                       usRpcId |= *pucData++; lSize--;
+               }
+               usRpcLen = *pucData++; lSize--;
+               if (usRpcLen&0x80)
+               {
+                       usRpcLen &= ~0x80;
+                       usRpcLen <<= 8;
+                       usRpcLen |= *pucData++; lSize--;
+               }
+               pucData += usRpcLen;
+               lSize -= usRpcLen;
+       }
+       return lSize == 0;
+}
+/*
+ * RpcEngine_OnPacketReceived - copied from RpcEngine::OnPacketReceived
+ *
+ */
+static int BbdBridge_OnPacketReceived(unsigned char *pucData, unsigned short usSize)
+{
+       int sensor = 0, gnss = 0;
+       if (BbdBridge_CheckPacketSanity(pucData, usSize))
+       {
+               long lSize = (long)usSize;
+               while (lSize > 0)
+               {
+#ifdef DEBUG_1HZ_STAT
+                       unsigned char *pucDataOrg = pucData;
+#endif
+                       unsigned short usRpcId    = *pucData++;
+                       unsigned short usRpcLen;
+                       lSize--;
+                       if (usRpcId&0x80)
+                       {
+                               usRpcId &= ~0x80;
+                               usRpcId <<= 8;
+                               usRpcId |= *pucData++; lSize--;
+                       }
+                       usRpcLen = *pucData++; lSize--;
+                       if (usRpcLen&0x80)
+                       {
+                               usRpcLen &= ~0x80;
+                               usRpcLen <<= 8;
+                               usRpcLen |= *pucData++; lSize--;
+                       }
+                       if (BbdBridge_OnRpcReceived(usRpcId, pucData, usRpcLen))
+                               sensor++;
+                       else
+                               gnss++;
+#ifdef DEBUG_1HZ_STAT
+                       bbd_update_stat(STAT_RX_RPC, usRpcLen+pucData-pucDataOrg);
+#endif
+                       pucData += usRpcLen;
+                       lSize -= usRpcLen;
+               }
+       }
+       else
+       {
+               WARN_ON(1);
+       }
+       return (sensor > 0);
+}
+
+static bool TransportLayer_PacketReceived(void *priv)
+{
+       unsigned char ucCrc = 0;
+       /* minimum is seqId, payload size, flags, and Crc */
+       if (m_uiRxLen >= 4)
+       {
+               /* compute CRC */
+               /* CRC is not applied on itself, nor on the SeqId */
+               ucCrc = crc_calc_many(&ucCrc, &m_aucRxMessageBuf[1], m_uiRxLen-2);
+               /* CRC has its nibble inverted for stronger CRC (as CRC of
+                * a packet with itself is always 0, if EoP is not detected,
+                * that always reset the CRC)
+                */
+               ucCrc = ((ucCrc&0x0F)<<4) | ((ucCrc&0xF0)>>4);
+               if (ucCrc != m_aucRxMessageBuf[m_uiRxLen-1]) /* CRC is last */
+               {
+                       return false;
+               }
+       }
+       else
+       {
+               return false;
+       }
+#ifdef DEBUG_1HZ_STAT
+       bbd_update_stat(STAT_RX_TL, m_uiRxLen);
+#endif
+       /* passed CRC check */
+       {
+               unsigned char *pucData = &m_aucRxMessageBuf[0];
+               unsigned short usLen = m_uiRxLen-1;
+               unsigned char ucSeqId = *pucData++;             /* usLen--; */
+               unsigned short usPayloadSize = *pucData++;      /* usLen--; */
+               unsigned short usFlags = *pucData++;            /* usLen--; */
+               bool bReliablePacket = false;
+               unsigned char ucReliableSeqId=0;
+               bool bReliableAckReceived = false;
+               unsigned char ucReliableAckSeqId=0;
+               bool bReliableNackReceived = false;
+               unsigned char ucReliableNackSeqId=0;
+               bool bAckReceived = false;
+               bool bInternalPacket = false;
+               bool bIgnoreSeqId = false;
+               unsigned short usAckFlags = 0;
+               unsigned int i=0;
+               usLen -= 3;
+               for (i = 0; i < 16 && usFlags != 0 && usLen > 0; ++i)
+               {
+                       unsigned short usFlagMask = (1 << i);
+                       unsigned short usFlagBit = usFlags & usFlagMask;
+                       unsigned char ucFlagDetail = 0;
+                       if (usFlagBit == 0)
+                       {
+                               continue;
+                       }
+                       usFlags     &= ~usFlagBit;   /* clear the flag */
+                       ucFlagDetail = *pucData++;   /* extract flag details */
+                       --usLen;
+                       if (usFlagBit == FLAG_PACKET_ACK) /* acknowledgement */
+                       {
+                               /* flag detail contain the acknowledged SeqId */
+                               unsigned char ucReceivedAckSeqId = ucFlagDetail;
+                               m_ucLastAckSeqId = ucReceivedAckSeqId;
+                               bAckReceived = true;
+                       }
+                       else if (usFlagBit == FLAG_RELIABLE_PACKET)
+                       {
+                               /* This is a reliable packet. we need to provide the proper Ack */
+                               ucReliableSeqId = ucFlagDetail;
+                               bReliablePacket = true;
+                       }
+                       else if (usFlagBit == FLAG_RELIABLE_ACK)
+                       {
+                               bReliableAckReceived = true;
+                               ucReliableAckSeqId = ucFlagDetail;
+                       }
+                       else if (usFlagBit == FLAG_RELIABLE_NACK)
+                       {
+                               bReliableNackReceived = true;
+                               ucReliableNackSeqId = ucFlagDetail;
+                       }
+                       else if (usFlagBit == FLAG_MSG_LOST)
+                       {
+                               /* remote TransportLayer lost had some SeqId jumps */
+                               m_otCurrentStats.ulRemotePacketLost += ucFlagDetail;
+                       }
+                       else if (usFlagBit == FLAG_MSG_GARBAGE)
+                       {
+                               /* remote TransportLayer detected garbage */
+                               m_otCurrentStats.ulRemoteGarbage += ucFlagDetail;
+                       }
+                       else if (usFlagBit == FLAG_SIZE_EXTENDED)
+                       {
+                               /* flag detail contains the MSB of the payload size */
+                               usPayloadSize |= (ucFlagDetail<<8);
+                       }
+                       else if (usFlagBit == FLAG_EXTENDED)
+                       {
+                               /* the flags are extended, which means that the details
+                                * contains the MSB of the 16bit flags
+                                */
+                               usFlags |= (ucFlagDetail<<8);
+                       }
+                       else if (usFlagBit == FLAG_INTERNAL_PACKET)
+                       {
+                               /* don't care about details */
+                               bInternalPacket = true;
+                       }
+                       else if (usFlagBit == FLAG_IGNORE_SEQID)
+                       {
+                               /* don't care about details */
+                               bIgnoreSeqId = true;
+                       }
+                       else
+                       {
+                               /* we did not process the flag, just put it back
+                                * this is an error, so we can break now, as there is no
+                                * point in continuing
+                                */
+                               usFlags |= usFlagBit;
+                               break;
+                       }
+               }
+               /* if flag is not garbage, entire packet should all be consumed */
+               /* remaining length of the buffer should be the payload size */
+               if (usFlags == 0 &&
+                               usPayloadSize == usLen)
+               {
+                       /* we now have a valid packet (at least it passed all our
+                        * validity checks, so we are going to trust it
+                        */
+                       unsigned char ucExpectedTxSeqId = (m_ucLastRxSeqId+1)&0xFF;
+                       if (ucSeqId != ucExpectedTxSeqId
+                                       && !bIgnoreSeqId
+                                       && !m_bOngoingSync)
+                       {
+                               /* Some packets were lost, jump in the RxSeqId */
+                               m_otCurrentStats.ulRxPacketLost +=
+                                       ((ucSeqId - ucExpectedTxSeqId)&0xFF);
+                       }
+                       m_ucLastRxSeqId = ucSeqId; /* increase expected SeqId */
+                       if (!bAckReceived || usLen > 0 )
+                       {
+                               bool bDelayedEnough = (++m_ucDelayAckCount > 200);
+                               ++m_otCurrentStats.ulPacketReceived;
+                               /* if this is a payload packet, we need to acknowledge it.
+                                * But don't clog the wires with power-hungry simple
+                                * acks unless we have too many (200) outstanding.
+                                */
+                               if (bDelayedEnough)
+                               {
+                                       pr_debug("Skip averted/%d %s %s\n", __LINE__,
+                                                       (bAckReceived  ) ? "ACK"  : "!ack",
+                                                       (bDelayedEnough) ? "ENUF" : "!enuf");
+                                       usAckFlags |= FLAG_PACKET_ACK;
+                                       m_ucDelayAckCount = 0;
+                               }
+                               else
+                               {
+                                       pr_debug("Skip/%d %s %s %d\n", __LINE__,
+                                                       (bAckReceived  ) ? "ACK"  : "!ack",
+                                                       (bDelayedEnough) ? "ENUF" : "!enuf",
+                                                       m_ucDelayAckCount);
+                               }
+                       }
+                       else
+                       {
+                               ++m_otCurrentStats.ulAckReceived;
+                       }
+                       {
+                               bool bProcessPacket = bReliablePacket || usLen > 0;
+                               if (bReliablePacket)
+                               {
+                                       pr_debug("TransportLayer_Received Reliable(Size %u, SeqId %u)\n", usLen, ucReliableSeqId);
+                                       /* if this is a reliable message, we need to make sure
+                                        * we didn't received it before!
+                                        * if we did, the Host probably didn't received the Ack,
+                                        * so let's just send the ack
+                                        * Reliable seqId is not enough, so we also use CRC and
+                                        * Lenght to confirm this was the same message received!
+                                        */
+                                       if (ucReliableSeqId == m_ucReliableSeqId
+                                                       && ucCrc == m_ucReliableCrc
+                                                       && usLen == m_usReliableLen)
+                                       {
+                                               /* already received that packet, remote TransportLayer
+                                                * probably lost the Acknowledgement, send it again
+                                                */
+                                               usAckFlags |= FLAG_PACKET_ACK | FLAG_RELIABLE_ACK;
+                                               bProcessPacket = false; /* we should not process it again */
+                                       }
+                                       else if (ucReliableSeqId == ((m_ucReliableSeqId+1)&0xFF))
+                                       {
+                                               /* this is a valid message, just do nothing but update
+                                                * the reliable info. the message will be processed below
+                                                */
+                                               usAckFlags |= FLAG_PACKET_ACK | FLAG_RELIABLE_ACK;
+                                               /* already received that packet, remote TransportLayer
+                                                * probably lost the Acknowledgement, send it again
+                                                */
+                                               m_usReliableLen   = usLen;
+                                               m_ucReliableSeqId = ucReliableSeqId;
+                                               m_ucReliableCrc   = ucCrc;
+                                               m_otCurrentStats.ulReliablePacketReceived++;
+                                       }
+                                       else
+                                       {
+                                               /* we received the wrong reliable SeqId */
+                                               usAckFlags |= FLAG_PACKET_ACK | FLAG_RELIABLE_NACK;
+                                               bProcessPacket = false; /* we cannot accept the packet */
+                                       }
+                               }
+                               if (bProcessPacket)
+                               {
+                                       if (bInternalPacket)
+                                       {
+                                               bcm_on_packet_recieved(priv, m_aucRxEscapedBuf, m_uiEscLen);
+                                       }
+                                       else
+                                       {
+                                               /* everything good, notify upper layer that we have
+                                                * the payload of a packet available
+                                                In case of sensor data, we don't want send it to lhd! */
+                                               if (!BbdBridge_OnPacketReceived(pucData, usLen))
+                                                       bcm_on_packet_recieved(priv, m_aucRxEscapedBuf, m_uiEscLen);
+                                       }
+                               }
+                               else
+                               {
+                                       bcm_on_packet_recieved(priv, m_aucRxEscapedBuf, m_uiEscLen);
+                               }
+                               return true;
+                       }
+               }
+               else
+               {
+                       return false;
+               }
+       }
+}
+
+/*
+ * bbd_parse_asic_data  - copied from TransportLayer::ParseIncomingData
+ *
+ */
+void bbd_parse_asic_data(unsigned char *pucData, unsigned short usLen, void (*to_gpsd)(unsigned char *packet, unsigned short len, void* priv), void* priv)
+{
+       unsigned short usIdx=0;
+       while (usIdx != usLen)
+       {
+               unsigned char ucData = pucData[usIdx++];
+               m_ulByteCntSinceLastValidPacket++;
+               if (sizeof(m_aucRxEscapedBuf) > m_uiEscLen)
+                       m_aucRxEscapedBuf[m_uiEscLen++] = ucData;
+
+               if (ucData == XON_CHARACTER || ucData == XOFF_CHARACTER)
+               {
+                       continue;
+               }
+               switch(m_uiParserState)
+               {
+                       case WAIT_FOR_ESC_SOP:
+                               {
+                                       if (ucData == ESCAPE_CHARACTER)
+                                       {
+                                               m_uiParserState = WAIT_FOR_SOP;
+                                               m_otCurrentStats.ulRxGarbageBytes += (m_ulByteCntSinceLastValidPacket -1); /* if we had only one byte, then there is no garbage, any extra is garbage */
+                                               m_ulByteCntSinceLastValidPacket = 1;
+                                       }
+                               }
+                               break;
+                       case WAIT_FOR_SOP:
+                               {
+                                       if (ucData == SOP_CHARACTER)
+                                       {
+                                               m_uiParserState = WAIT_FOR_MESSAGE_COMPLETE;
+                                               m_uiRxLen = 0;
+                                       }
+                                       else
+                                       {
+                                               if (ucData != ESCAPE_CHARACTER)
+                                               {
+                                                       m_uiParserState = WAIT_FOR_ESC_SOP;
+                                               }
+                                               else
+                                               {
+                                                       m_otCurrentStats.ulRxGarbageBytes += 2;
+                                                       m_ulByteCntSinceLastValidPacket = 1;
+                                               }
+                                       }
+                               }
+                               break;
+                       case WAIT_FOR_MESSAGE_COMPLETE:
+                               {
+                                       if (ucData == ESCAPE_CHARACTER)
+                                       {
+                                               m_uiParserState = WAIT_FOR_EOP;
+                                       }
+                                       else if (m_uiRxLen < sizeof(m_aucRxMessageBuf))
+                                       {
+                                               m_aucRxMessageBuf[m_uiRxLen++] = ucData;
+                                       }
+                                       else
+                                       {
+                                               m_uiParserState = WAIT_FOR_ESC_SOP;
+                                       }
+                               }
+                               break;
+                       case WAIT_FOR_EOP:
+                               {
+                                       if (ucData == EOP_CHARACTER)
+                                       {
+                                               if (TransportLayer_PacketReceived(priv))
+                                               {
+                                                       m_ulByteCntSinceLastValidPacket = 0; /* we had a valid packet, restart the cnt */
+                                               }
+                                               m_uiEscLen = 0;
+                                               m_uiParserState = WAIT_FOR_ESC_SOP;
+                                       }
+                                       else if (ucData == ESCAPED_ESCAPE_CHARACTER)
+                                       {
+                                               if (m_uiRxLen < _DIM(m_aucRxMessageBuf))
+                                               {
+                                                       m_aucRxMessageBuf[m_uiRxLen++] = ESCAPE_CHARACTER;
+                                                       m_uiParserState = WAIT_FOR_MESSAGE_COMPLETE;
+                                               }
+                                               else
+                                               {
+                                                       m_uiParserState = WAIT_FOR_ESC_SOP;
+                                               }
+                                       }
+                                       else if (ucData == ESCAPED_XON_CHARACTER)
+                                       {
+                                               if (m_uiRxLen < _DIM(m_aucRxMessageBuf))
+                                               {
+                                                       m_aucRxMessageBuf[m_uiRxLen++] = XON_CHARACTER;
+                                                       m_uiParserState = WAIT_FOR_MESSAGE_COMPLETE;
+                                               }
+                                               else
+                                               {
+                                                       m_uiParserState = WAIT_FOR_ESC_SOP;
+                                               }
+                                       }
+                                       else if (ucData == ESCAPED_XOFF_CHARACTER)
+                                       {
+                                               if (m_uiRxLen < _DIM(m_aucRxMessageBuf))
+                                               {
+                                                       m_aucRxMessageBuf[m_uiRxLen++] = XOFF_CHARACTER;
+                                                       m_uiParserState = WAIT_FOR_MESSAGE_COMPLETE;
+                                               }
+                                               else
+                                               {
+                                                       m_uiParserState = WAIT_FOR_ESC_SOP;
+                                               }
+                                       }
+                                       else if (ucData == SOP_CHARACTER)
+                                       {
+                                               /* we probably missed the ESC EOP, but we start receiving a new packet! */
+                                               m_uiParserState = WAIT_FOR_MESSAGE_COMPLETE;
+                                               /* init the parser! */
+                                               m_uiRxLen = 0;
+                                               m_uiEscLen = 0;
+
+                                               m_otCurrentStats.ulRxGarbageBytes += (m_ulByteCntSinceLastValidPacket-2); /* if we had only one byte, then there is no garbage, any extra is garbage */
+                                               m_ulByteCntSinceLastValidPacket = 2;
+                                       }
+                                       else if (ucData == ESCAPE_CHARACTER)
+                                       {
+                                               m_uiParserState = WAIT_FOR_SOP;
+                                       }
+                                       else
+                                       {
+                                               m_uiParserState = WAIT_FOR_ESC_SOP;
+                                       }
+                               }
+                               break;
+               }
+       }
+}
diff --git a/drivers/sensors/brcm/bbdpl2/bcm_dbg.c b/drivers/sensors/brcm/bbdpl2/bcm_dbg.c
new file mode 100644 (file)
index 0000000..97ef8ed
--- /dev/null
@@ -0,0 +1,33 @@
+#include <kernel.h>
+#include <linux/printk.h>
+#include <linux/time.h>
+
+#define MAX_DBG_LOG_LEN 60
+#define MAX_DBG_RECORD (4096*128)
+
+struct dbg_record
+{
+       unsigned long time;
+       char log[MAX_DBG_LOG_LEN];
+};
+
+static int dbg_idx;
+static struct dbg_record rec[MAX_DBG_RECORD];
+
+void record(const char *str)
+{
+       struct timeval tv;
+       do_gettimeofday(&tv);
+       rec[dbg_idx].time = tv.tv_sec*1000000UL + tv.tv_usec;
+       strncpy(rec[dbg_idx].log, str, MAX_DBG_LOG_LEN);
+       dbg_idx = ++dbg_idx&(MAX_DBG_RECORD-1);
+}
+
+void print_record(void)
+{
+       int i;
+
+       for (i=0; i<MAX_DBG_RECORD; i++) {
+               printk("<%lu>\t%s\n", rec[i].time, rec[i].log);
+       }
+}
diff --git a/drivers/sensors/brcm/bbdpl2/bcm_gps_i2c.c b/drivers/sensors/brcm/bbdpl2/bcm_gps_i2c.c
new file mode 100644 (file)
index 0000000..08a8f94
--- /dev/null
@@ -0,0 +1,741 @@
+/******************************************************************************
+ * Copyright (C) 2013 Broadcom Corporation
+ *
+ *
+ * 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.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ ******************************************************************************/
+
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <linux/uaccess.h>
+#include <linux/poll.h>
+#include <linux/miscdevice.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/platform_device.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/list.h>
+#include <linux/i2c.h>
+#include <linux/i2c-dev.h>
+#include <linux/io.h>
+#include <linux/version.h>
+#include <linux/workqueue.h>
+#include <linux/unistd.h>
+#include <linux/bug.h>
+#include <linux/mutex.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include "bcm_gps_i2c.h"
+#include <linux/of_gpio.h>
+
+#define GPS_VERSION    "2.23"
+#define PFX            "bcmgps:"
+
+#ifdef CONFIG_SENSORS_SSP_BBD
+void bbd_parse_asic_data(unsigned char *pucData, unsigned short usLen, void (*to_gpsd)(unsigned char *packet, unsigned short len, void* priv), void* priv);
+#endif
+
+/* ring buffer functions */
+typedef struct ring_buffer {
+       struct mutex *lock;
+       int start;
+       int end;
+       unsigned int size_of_ring_buffer;
+       unsigned char *buffer;
+       int  (*push_back_byte)(struct ring_buffer *self, unsigned char byte);
+       int  (*pop_front_byte)(struct ring_buffer *self, unsigned char *byte);
+       int  (*push_back)(struct ring_buffer *self, unsigned char *buffer, unsigned int sizeOfBuffer);
+       int  (*pop_front)(struct ring_buffer *self, unsigned char *buffer, unsigned int sizeOfBuffer);
+       int  (*is_empty_internal)(struct ring_buffer *self);
+       int  (*is_full_internal)(struct ring_buffer *self);
+       int  (*is_empty)(struct ring_buffer *self);
+       int  (*is_full)(struct ring_buffer *self);
+       void (*reset)(struct ring_buffer *self);
+} ring_buffer;
+
+static ring_buffer *ring_buffer_init(int size_of_ring_buffer, struct mutex *lock);
+static void ring_buffer_free(ring_buffer *self);
+static int  ring_buffer_pushBackByte(struct ring_buffer *self, unsigned char byte);
+static int  ring_buffer_popFrontByte(ring_buffer *self, unsigned char *byte);
+static int  ring_buffer_pushBack(ring_buffer *self, unsigned char *buffer, unsigned int sizeOfBuffer);
+static int  ring_buffer_popFront(ring_buffer *self, unsigned char *buffer, unsigned int sizeOfBuffer);
+static int  ring_buffer_isEmptyInternal(ring_buffer *self);
+static int  ring_buffer_isFullInternal(ring_buffer *self);
+static int  ring_buffer_isEmpty(ring_buffer *self);
+static int  ring_buffer_isFull(ring_buffer *self);
+static void ring_buffer_reset(ring_buffer *self);
+
+#define MAX_RX_PCKT_LEN  256
+#define MAX_TX_PCKT_LEN  256
+
+#define MAX_TEMP_READ_BUFFER (256 * 2)
+
+#define SIZE_OF_READ_RING_BUFFER 256*128
+#define SIZE_OF_WRITE_RING_BUFFER 256*64
+
+static DEFINE_MUTEX(gps_lock);
+static DEFINE_MUTEX(gps_read_lock);
+static DEFINE_MUTEX(gps_write_lock);
+
+struct gps_irq {
+       wait_queue_head_t wait;
+       int irq;
+       int host_req_pin;
+       struct miscdevice misc;
+       struct i2c_client *client;
+       ring_buffer *read_rbuf;
+       ring_buffer *write_rbuf;
+       struct work_struct read_task;
+       struct work_struct write_task;
+       int is_opened;
+};
+
+
+bool ssi_dbg;
+
+static unsigned long init_time = 0;
+static unsigned long clock_get_ms(void)
+{
+       struct timeval t;
+       unsigned long now;
+
+       do_gettimeofday(&t);
+       now = t.tv_usec / 1000 + t.tv_sec * 1000;
+       if ( init_time == 0 )
+               init_time = now;
+
+       return now - init_time;
+}
+
+static void pk_log(char* dir, unsigned char* data, int len)
+{
+       char acB[960];
+       char *p = acB;
+       int  the_trans = len;
+       int i,j,n;
+
+       char ic = 'D';
+       char xc = dir[0] == 'r' || dir[0] == 'w' ? 'x':'X';
+
+       if (likely(!ssi_dbg))
+               return;
+
+       n = len;
+       p += snprintf(acB,sizeof(acB),"#%06ld%c %2s,      %5d: ",
+                       clock_get_ms() % 1000000,ic, dir, n);
+
+       for (i=0, n=32; i<len; i=j, n=32)
+       {
+               for(j = i; j < (i + n) && j < len && the_trans != 0; j++,the_trans--) {
+                       p += snprintf(p,sizeof(acB) - (p - acB), "%02X ", data[j]);
+               }
+               pr_info("%s\n",acB);
+               if(j < len) {
+                       p = acB;
+                       if ( the_trans == 0 )  { dir[0] = xc; the_trans--; }
+                       p += snprintf(acB,sizeof(acB),"         %2s              ",dir);
+               }
+       }
+
+       if (i==0)
+               pr_info("%s\n",acB);
+}
+
+void read_work(struct work_struct *work)
+{
+       struct gps_irq *ac_data =
+               container_of(work, struct gps_irq, read_task);
+
+       int number_of_read = 0;
+       unsigned char size_of_packet = 0;
+       unsigned char temp_read_buffer[MAX_RX_PCKT_LEN] = {0};
+       char host_req_gpio_value = 0;
+
+       while(1)
+       {
+               /* 1. check conditions. */
+               if (ac_data->is_opened == 0) break;
+
+               host_req_gpio_value = gpio_get_value(ac_data->host_req_pin);
+               if (host_req_gpio_value == 0) break;
+
+               /* 2. Initialize values. */
+               size_of_packet = 0;
+               memset(temp_read_buffer, 0x00, MAX_RX_PCKT_LEN);
+
+               /* 3. Read the length of message. */
+               number_of_read = i2c_master_recv(ac_data->client, &size_of_packet, 1);
+               pk_log("r", &size_of_packet, 1);
+
+               /* 4. check the length, if it is zero, sender doesn't have data anymore. */
+               if (size_of_packet == 0) break;
+
+               /* 5. Read message. */
+               number_of_read = i2c_master_recv(ac_data->client, temp_read_buffer, (size_of_packet + 1));
+               pk_log("r", temp_read_buffer, size_of_packet+1);
+
+               /* 6. push message into ring buffer. */
+               /*    -- push message without first byte that is the length of packet. */
+               ac_data->read_rbuf->push_back(ac_data->read_rbuf, &(temp_read_buffer[1]), size_of_packet);
+
+               /* 6-1. Call BBD */
+#ifdef CONFIG_SENSORS_SSP_BBD
+               bbd_parse_asic_data(&temp_read_buffer[1], size_of_packet, NULL, NULL);
+#endif
+
+               /* 7. wake up the task for poll. */
+               wake_up_interruptible(&ac_data->wait);
+       }
+}
+
+void write_work(struct work_struct *work)
+{
+       struct gps_irq *ac_data =
+               container_of(work, struct gps_irq, write_task);
+
+       int number_of_sent = 0;
+       int size_of_packet = 0;
+       unsigned char temp_write_buffer[MAX_TX_PCKT_LEN] = {0};
+       int number_of_i2c_sent = 0;
+
+       while(1)
+       {
+               /* 1. check condition */
+               if (ac_data->write_rbuf->is_empty(ac_data->write_rbuf)) break;
+               if (ac_data->is_opened == 0) break;
+
+               /* 2. initialize values */
+               memset(temp_write_buffer, 0x00, MAX_TX_PCKT_LEN);
+
+               /* 3. read data from ring buffer */
+               size_of_packet = ac_data->write_rbuf->pop_front(ac_data->write_rbuf, temp_write_buffer, MAX_TX_PCKT_LEN);
+
+               /* 4. send them through i2c */
+               number_of_i2c_sent = i2c_master_send(ac_data->client, temp_write_buffer, size_of_packet);
+               pk_log("w", temp_write_buffer, size_of_packet);
+
+               number_of_sent += size_of_packet;
+
+       }
+
+       pr_debug(PFX PFX "write_work() : addr = 0x%02X, sent %d bytes.\n", ac_data->client->addr, number_of_sent);
+}
+
+irqreturn_t gps_irq_handler(int irq, void *dev_id)
+{
+       struct gps_irq *ac_data = dev_id;
+       char gpio_value = 0x00;
+
+       gpio_value = gpio_get_value(ac_data->host_req_pin);
+
+       /* If HOST_REQ is set, start to read data. */
+       if (gpio_value)
+               schedule_work(&ac_data->read_task);
+
+       return IRQ_HANDLED;
+}
+
+static int gps_irq_open(struct inode *inode, struct file *filp)
+{
+       struct gps_irq *ac_data = container_of(filp->private_data,
+                       struct gps_irq,
+                       misc);
+       int ret = 0;
+
+       mutex_lock(&gps_lock);
+       if (ac_data->is_opened == 1)
+       {
+               ret = -EBUSY;
+       }
+       else
+       {
+               ac_data->is_opened = 1;
+       }
+       mutex_unlock(&gps_lock);
+
+       if (ret < 0)
+       {
+               pr_err(PFX "open error(%d)\n", ret);
+               return ret;
+       }
+
+       pr_notice(PFX "gps_irq_open() : 0x%p\n", ac_data);
+
+       filp->private_data = ac_data;
+
+       ac_data->read_rbuf->reset(ac_data->read_rbuf);
+       ac_data->write_rbuf->reset(ac_data->write_rbuf);
+
+       return ret;
+}
+
+static int gps_irq_release(struct inode *inode, struct file *filp)
+{
+       struct gps_irq *ac_data = filp->private_data;
+
+       mutex_lock(&gps_lock);
+       ac_data->is_opened  = 0;
+       mutex_unlock(&gps_lock);
+
+       filp->private_data = ac_data;
+
+       pr_notice(PFX "gps_irq_release() ac_data = 0x%p\n", ac_data);
+
+       return 0;
+}
+
+static unsigned int gps_irq_poll(struct file *file, poll_table *wait)
+{
+       struct gps_irq *ac_data = file->private_data;
+
+       BUG_ON(!ac_data);
+
+       poll_wait(file, &ac_data->wait, wait);
+
+       if (!(ac_data->read_rbuf->is_empty(ac_data->read_rbuf)))
+               return POLLIN | POLLRDNORM;
+
+       return 0;
+}
+
+static ssize_t gps_irq_read(struct file *file, char __user *buf,
+               size_t count, loff_t *offset)
+{
+       struct gps_irq *ac_data = file->private_data;
+       int number_of_read = 0;
+       int size_of_pop = 0;
+       int number_of_popped = 0;
+       unsigned char temp_read_buffer[MAX_TEMP_READ_BUFFER] = {0};
+
+       while (1)
+       {
+               /* 1. check condition */
+               if (ac_data->read_rbuf->is_empty(ac_data->read_rbuf)) break; /* no more data in ring buffer */
+               if (number_of_read >= count) break; /* user buf is full. */
+
+               /* 2. initialize values */
+               memset(temp_read_buffer, 0x00, MAX_RX_PCKT_LEN);
+
+               /* 3. read data from ring buffer */
+               size_of_pop = (count - number_of_read) < MAX_TEMP_READ_BUFFER ? (count - number_of_read) : MAX_TEMP_READ_BUFFER;
+               number_of_popped = ac_data->read_rbuf->pop_front(ac_data->read_rbuf, temp_read_buffer, size_of_pop);
+
+               /* 4. copy data to user */
+               if (copy_to_user((buf + number_of_read), temp_read_buffer, number_of_popped))
+               {
+                       /* reset ring buffer */
+                       ac_data->read_rbuf->reset(ac_data->read_rbuf);
+                       return -EFAULT;
+               }
+               number_of_read += number_of_popped;
+       }
+
+       pr_debug(PFX "gps_irq_read() : # of read = %d\n", number_of_read);
+
+       return number_of_read;
+}
+
+static ssize_t gps_irq_write(struct file *file, const char __user *buf,
+               size_t count, loff_t *offset)
+{
+       struct gps_irq *ac_data = file->private_data;
+
+       unsigned char temp_write_buffer[MAX_TX_PCKT_LEN] = {0};
+       int number_of_sent = 0;
+       int number_of_push = 0;
+       int number_of_pushed = 0;
+
+       while (1)
+       {
+               /* 1. initialize values */
+               memset(temp_write_buffer, 0x00, MAX_TX_PCKT_LEN);
+
+               /* 2. copy buf to kernel */
+               number_of_push = (count - number_of_sent) < MAX_TX_PCKT_LEN ? (count - number_of_sent) : MAX_TX_PCKT_LEN;
+               if (copy_from_user(temp_write_buffer, (buf + number_of_sent), number_of_push))
+               {
+                       /* reset write ring buffer. */
+                       ac_data->write_rbuf->reset(ac_data->write_rbuf);
+                       return -EFAULT;
+               }
+               /* 3. push data to ring buffer */
+               number_of_pushed = ac_data->write_rbuf->push_back(ac_data->write_rbuf, temp_write_buffer, number_of_push);
+               number_of_sent += number_of_pushed;
+
+               /* 4. check escape condition */
+               if (number_of_sent >= count) break; /* done */
+               if (number_of_push != number_of_pushed) break; /* write_rbuf is full. */
+       }
+
+       /* 4. schedule write work */
+       schedule_work(&ac_data->write_task);
+
+       pr_debug(PFX "gps_irq_write() : writing %d bytes returns %d\n", count, number_of_sent);
+
+       return number_of_sent;
+}
+
+
+static long gps_irq_ioctl( struct file *filp,
+               unsigned int cmd, unsigned long arg)
+{
+       struct gps_irq *ac_data = filp->private_data;
+       struct i2c_client *client = ac_data->client;
+
+       BUG_ON(!client);
+
+       switch (cmd) {
+               case I2C_SLAVE:
+               case I2C_SLAVE_FORCE:
+                       if (arg > 0x7f)
+                       {
+                               pr_err(PFX "out of range: 0x%x should be less than 0x7f.", (unsigned int)arg);
+                               return -EINVAL;
+                       }
+                       client->addr = arg;
+                       pr_info(PFX "ioctl: client->addr = 0x%x\n", client->addr);
+                       break;
+
+               case I2C_RETRIES:
+                       client->adapter->retries = arg;
+                       pr_info("ioctl, client->adapter->retries = %d\n",client->adapter->retries);
+                       break;
+
+               case I2C_TIMEOUT:
+                       /* For historical reasons, user-space sets the timeout
+                        * value in units of 10 ms.
+                        */
+                       client->adapter->timeout = msecs_to_jiffies(arg * 10);
+                       pr_info(PFX "ioctl, client->adapter->timeout = %d\n",client->adapter->timeout);
+                       break;
+               default:
+                       return -ENOTTY;
+       }
+       return 0;
+}
+
+static const struct file_operations gps_irq_fops = {
+       .owner = THIS_MODULE,
+       .open = gps_irq_open,
+       .release = gps_irq_release,
+       .poll = gps_irq_poll,
+       .read = gps_irq_read,
+       .write = gps_irq_write,
+       .unlocked_ioctl = gps_irq_ioctl
+};
+
+static int gps_hostwake_probe(struct i2c_client *client,
+               const struct i2c_device_id *id)
+{
+       struct bcm_gps_platform_data *pdata = (struct bcm_gps_platform_data*)client->dev.platform_data;
+       struct gps_irq *ac_data = kzalloc(sizeof(struct gps_irq), GFP_KERNEL);
+
+       int irq = -1;
+       int ret = -1;
+       int err = -1;
+       unsigned int gps_gpio_i2c = 0;
+
+#ifdef CONFIG_OF
+       enum of_gpio_flags flags;
+       if (!client->dev.of_node) {
+               pr_err(PFX "of_node of 475x i2c device is NULL\n");
+               return -ENODEV;
+       }
+       gps_gpio_i2c = of_get_named_gpio_flags(client->dev.of_node, "brcm,irq_gpio", 0, &flags);
+       if (gps_gpio_i2c < 0) {
+               pr_err(PFX "475x host wake gpio %d err\n", gps_gpio_i2c);
+               return -1;
+       }
+       pr_notice(PFX "%s pdata->gpio_i2c = %d, ac_data = 0x%p\n",__func__, gps_gpio_i2c, ac_data);
+#else
+       gps_gpio_i2c = pdata->gpio_i2c;
+       pr_notice(PFX "%s pdata->gpio_i2c = %d, ac_data = 0x%p\n",__func__, pdata->gpio_i2c, ac_data);
+#endif
+
+       ac_data->read_rbuf  = ring_buffer_init(SIZE_OF_READ_RING_BUFFER, &gps_read_lock);
+       ac_data->write_rbuf = ring_buffer_init(SIZE_OF_WRITE_RING_BUFFER, &gps_write_lock);
+       ac_data->is_opened  = 0;
+
+       pr_notice(PFX "%s\n",__func__);
+
+       init_waitqueue_head(&ac_data->wait);
+
+       if ((err = gpio_request(gps_gpio_i2c, "gps_irq"))) {
+               pr_warning(PFX "Can't request HOST_REQ GPIO %d.It may be already registered in init.xyz.3rdparty.rc/init.xyz.rc\n",gps_gpio_i2c);
+       }
+       gpio_export(gps_gpio_i2c, 1);
+       gpio_direction_input(gps_gpio_i2c);
+
+       irq = gpio_to_irq(gps_gpio_i2c);
+       if (irq < 0) {
+               pr_err(PFX KERN_ERR "Could not get GPS IRQ = %d!\n",gps_gpio_i2c);
+               gpio_free(gps_gpio_i2c);
+               return -EIO;
+       }
+
+       ac_data->irq = irq;
+       ac_data->host_req_pin = gps_gpio_i2c;
+       ret = request_irq(irq, gps_irq_handler,
+                       IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+                       /*KOM -- IRQF_TRIGGER_RISING, */
+                       "gps_interrupt",
+                       ac_data);
+
+       ac_data->client = client;
+
+       ac_data->misc.minor = MISC_DYNAMIC_MINOR;
+       ac_data->misc.name = "gps_irq";
+       ac_data->misc.fops = &gps_irq_fops;
+
+       pr_notice(PFX "%s misc register, name = %s, irq = %d, GPS gpio =  %d\n",__func__, ac_data->misc.name, irq, gps_gpio_i2c);
+       if (0 != (ret = misc_register(&ac_data->misc))) {
+               pr_err(PFX "cannot register gps miscdev on minor=%d (%d)\n",MISC_DYNAMIC_MINOR, ret);
+               free_irq(ac_data->irq, ac_data);
+               gpio_free(gps_gpio_i2c);
+               return ret;
+       }
+
+       INIT_WORK(&ac_data->read_task, read_work);
+       INIT_WORK(&ac_data->write_task, write_work);
+
+       i2c_set_clientdata(client, ac_data);
+
+       pr_notice(PFX "Initialized.\n");
+
+       return 0;
+}
+
+static int gps_hostwake_remove(struct i2c_client *client)
+{
+       struct bcm_gps_platform_data *pdata = 0;
+       struct gps_irq *ac_data = 0;
+
+       pr_notice(PFX " %s : called\n", __func__);
+
+       pdata = client->dev.platform_data;
+       gpio_free(pdata->gpio_i2c);
+
+       ac_data = i2c_get_clientdata(client);
+       free_irq(ac_data->irq, ac_data);
+       misc_deregister(&ac_data->misc);
+
+       cancel_work_sync(&ac_data->read_task);
+       cancel_work_sync(&ac_data->write_task);
+
+       ring_buffer_free(ac_data->read_rbuf);
+       ring_buffer_free(ac_data->write_rbuf);
+       kfree(ac_data);
+       return 0;
+}
+
+static const struct i2c_device_id gpsi2c_id[] = {
+       {"gpsi2c", 0},
+       {}
+};
+
+#ifdef CONFIG_OF
+static struct of_device_id gps_match_table[] = {
+       { .compatible = "brcm,gps",},
+       {},
+};
+#endif
+
+static struct i2c_driver gps_driver = {
+       .id_table = gpsi2c_id,
+       .probe = gps_hostwake_probe,
+       .remove = gps_hostwake_remove,
+       .driver = {
+               .owner = THIS_MODULE,
+               .name = "gpsi2c",
+#ifdef CONFIG_OF
+               .of_match_table = gps_match_table,
+#endif
+       },
+};
+
+static int gps_irq_init(void)
+{
+       pr_notice(PFX "Broadcom GPS i2c Driver v%s\n", GPS_VERSION);
+       return i2c_add_driver(&gps_driver);
+}
+
+static void gps_irq_exit(void)
+{
+       i2c_del_driver(&gps_driver);
+}
+
+/* ring_buffer implementation */
+static ring_buffer *ring_buffer_init(int size_of_ring_buffer, struct mutex *lock)
+{
+       ring_buffer *new_rb = 0;
+
+       new_rb = kzalloc(sizeof(ring_buffer), GFP_KERNEL);
+
+       new_rb->lock = lock;
+       new_rb->push_back_byte = ring_buffer_pushBackByte;
+       new_rb->pop_front_byte = ring_buffer_popFrontByte;
+       new_rb->push_back = ring_buffer_pushBack;
+       new_rb->pop_front = ring_buffer_popFront;
+       new_rb->is_empty_internal = ring_buffer_isEmptyInternal;
+       new_rb->is_full_internal = ring_buffer_isFullInternal;
+       new_rb->is_empty = ring_buffer_isEmpty;
+       new_rb->is_full = ring_buffer_isFull;
+       new_rb->reset = ring_buffer_reset;
+
+       new_rb->start = 0;
+       new_rb->end   = 0;
+       new_rb->size_of_ring_buffer = size_of_ring_buffer;
+
+       new_rb->buffer = kzalloc(new_rb->size_of_ring_buffer, GFP_KERNEL);
+
+       return new_rb;
+}
+
+static void ring_buffer_free(ring_buffer *self)
+{
+       self->start = 0;
+       self->end   = 0;
+       self->size_of_ring_buffer = 0;
+
+       kfree(self->buffer);
+       kfree(self);
+
+       self = 0;
+}
+
+static int  ring_buffer_pushBackByte(struct ring_buffer *self, unsigned char byte)
+{
+       if (self->is_full_internal(self)) return 0;
+
+       self->buffer[self->end] = byte;
+       self->end = (self->end + 1) % self->size_of_ring_buffer;
+
+       return 1;
+}
+
+static int  ring_buffer_popFrontByte(ring_buffer *self, unsigned char *byte)
+{
+       if (self->is_empty_internal(self)) return 0;
+
+       *byte = self->buffer[self->start];
+       self->start = (self->start + 1) % self->size_of_ring_buffer;
+
+       return 1;
+}
+
+static int  ring_buffer_pushBack(ring_buffer *self, unsigned char *buffer, unsigned int sizeOfBuffer)
+{
+       int i = 0;
+       int nPush = 0;
+
+       if (self->lock) mutex_lock(self->lock);
+       for (i = 0; i < sizeOfBuffer; i++)
+       {
+               if (self->push_back_byte(self, buffer[i]) == 0) break;
+               ++nPush;
+       }
+       if (self->lock) mutex_unlock(self->lock);
+
+       return nPush;
+}
+
+static int  ring_buffer_popFront(ring_buffer *self, unsigned char *buffer, unsigned int sizeOfBuffer)
+{
+       int i = 0;
+       int nPop = 0;
+       unsigned char byte;
+
+       if (self->lock) mutex_lock(self->lock);
+       for (i = 0; i < sizeOfBuffer; i++)
+       {
+               if (self->pop_front_byte(self, &byte) == 0) break;
+               buffer[i] = byte;
+               ++nPop;
+       }
+       if (self->lock) mutex_unlock(self->lock);
+
+       return nPop;
+}
+
+static int  ring_buffer_isEmptyInternal(ring_buffer *self)
+{
+       int ret = 0;
+
+       ret = (self->start == self->end);
+
+       return ret;
+}
+
+static int  ring_buffer_isFullInternal(ring_buffer *self)
+{
+       int ret = 0;
+
+       if ((self->start - 1) > 0)
+       {
+               ret = ((self->start - 1) == self->end);
+       }
+       else if (self->end == (self->size_of_ring_buffer - 1))
+       {
+               ret = 1;
+       }
+
+       return ret;
+}
+
+static int  ring_buffer_isEmpty(ring_buffer *self)
+{
+       int ret = 0;
+
+       if (self->lock) mutex_lock(self->lock);
+       ret = (self->start == self->end);
+       if (self->lock) mutex_unlock(self->lock);
+
+       return ret;
+}
+
+static int  ring_buffer_isFull(ring_buffer *self)
+{
+       int ret = 0;
+
+       if (self->lock) mutex_lock(self->lock);
+       if ((self->start - 1) > 0)
+       {
+               ret = ((self->start - 1) == self->end);
+       }
+       else if (self->end == (self->size_of_ring_buffer - 1))
+       {
+               ret = 1;
+       }
+       if (self->lock) mutex_unlock(self->lock);
+
+       return ret;
+}
+
+static void ring_buffer_reset(ring_buffer *self)
+{
+       if (self->lock) mutex_lock(self->lock);
+       self->start = 0;
+       self->end   = 0;
+       if (self->lock) mutex_unlock(self->lock);
+}
+
+module_init(gps_irq_init);
+module_exit(gps_irq_exit);
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Broadcom GPS Driver with host wake interrupt");
diff --git a/drivers/sensors/brcm/bbdpl2/bcm_gps_i2c.h b/drivers/sensors/brcm/bbdpl2/bcm_gps_i2c.h
new file mode 100644 (file)
index 0000000..08c40ca
--- /dev/null
@@ -0,0 +1,22 @@
+/******************************************************************************
+* Copyright (C) 2013 Broadcom Corporation
+*
+*
+* 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.
+*
+* This program is distributed "as is" WITHOUT ANY WARRANTY of any
+* kind, whether express or implied; without even the implied warranty
+* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+* GNU General Public License for more details.
+******************************************************************************/
+
+#ifndef _BCM_GPS_I2C_H_
+#define _BCM_GPS_I2C_H_
+
+struct bcm_gps_platform_data {
+    unsigned int gpio_i2c; /* HOST_REQ : to indicate that ASIC has data to send. */
+};
+
+#endif /* _BCM_GPS_I2C_H_ */
diff --git a/drivers/sensors/brcm/bbdpl2/bcm_gps_spi.c b/drivers/sensors/brcm/bbdpl2/bcm_gps_spi.c
new file mode 100644 (file)
index 0000000..8434aa1
--- /dev/null
@@ -0,0 +1,968 @@
+/******************************************************************************
+ * Copyright (C) 2015 Broadcom Corporation
+ *
+ *
+ * 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.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ ******************************************************************************/
+
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/sysrq.h>
+#include <linux/console.h>
+#include <linux/delay.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/spidev.h>
+#include <linux/kthread.h>
+#include <linux/circ_buf.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/poll.h>
+#include <linux/uaccess.h>
+#include <linux/wakelock.h>
+#include <linux/suspend.h>
+#include <linux/kernel.h>
+#include <linux/clk.h>
+#include <linux/ssp_platformdata.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/miscdevice.h>
+#include <linux/time.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <linux/kernel_stat.h>
+#include <linux/pm_runtime.h>
+
+#include "bbd.h"
+
+#define WORD_BURST_SIZE                        4
+#define CONFIG_SPI_DMA_BYTES_PER_WORD  4
+#define CONFIG_SPI_DMA_BITS_PER_WORD   32
+
+#define SSI_MODE_STREAM                0x00
+#define SSI_MODE_DEBUG         0x80
+
+#define SSI_MODE_HALF_DUPLEX   0x00
+#define SSI_MODE_FULL_DUPLEX   0x40
+
+#define SSI_WRITE_TRANS                0x00
+#define SSI_READ_TRANS         0x20
+
+#define SSI_WRITE_HD (SSI_WRITE_TRANS | SSI_MODE_HALF_DUPLEX)
+#define SSI_READ_HD  (SSI_READ_TRANS  | SSI_MODE_HALF_DUPLEX)
+
+#define DEBUG_TIME_STAT
+
+#ifdef CONFIG_SENSORS_SSP_BBD
+extern void bbd_parse_asic_data(unsigned char *pucData, unsigned short usLen, void (*to_gpsd)(unsigned char *packet, unsigned short len, void* priv), void* priv);
+#endif
+
+bool ssi_dbg;
+
+//--------------------------------------------------------------
+//
+//               Structs
+//
+//--------------------------------------------------------------
+
+#define BCM_SPI_READ_BUF_SIZE  (4*PAGE_SIZE)
+#define BCM_SPI_WRITE_BUF_SIZE (4*PAGE_SIZE)
+
+
+#define MAX_SPI_FRAME_LEN 254
+struct bcm_ssi_tx_frame
+{
+       unsigned char cmd;
+       unsigned char data[MAX_SPI_FRAME_LEN-1];
+} __attribute__((__packed__));
+
+struct bcm_ssi_rx_frame
+{
+       unsigned char status;
+       unsigned char len;
+       unsigned char data[MAX_SPI_FRAME_LEN-2];
+} __attribute__((__packed__));
+
+
+struct bcm_spi_priv
+{
+       struct spi_device *spi;
+
+       /* Char device stuff */
+       struct miscdevice misc;
+       bool busy;
+       struct circ_buf read_buf;
+       struct circ_buf write_buf;
+       struct mutex rlock;                     /* Lock for read_buf */
+       struct mutex wlock;                     /* Lock for write_buf */
+       char _read_buf[BCM_SPI_READ_BUF_SIZE];
+       char _write_buf[BCM_SPI_WRITE_BUF_SIZE];
+       wait_queue_head_t poll_wait;            /* for poll */
+
+       /* GPIO pins */
+       int host_req;
+       int mcu_req;
+       int mcu_resp;
+
+       /* IRQ and its control */
+       atomic_t irq_enabled;
+       spinlock_t irq_lock;
+
+       /* Work */
+       struct work_struct rxtx_work;
+       struct workqueue_struct *serial_wq;
+       atomic_t suspending;
+
+       /* SPI tx/rx buf */
+       struct bcm_ssi_tx_frame *tx_buf;
+       struct bcm_ssi_rx_frame *rx_buf;
+
+       struct wake_lock bcm_wake_lock;
+
+       struct clk *clk;
+};
+
+static struct bcm_spi_priv *g_bcm_gps;
+
+//--------------------------------------------------------------
+//
+//               File Operations
+//
+//--------------------------------------------------------------
+static int bcm_spi_open(struct inode *inode, struct file *filp)
+{
+       /* Initially, file->private_data points device itself and we can get our priv structs from it. */
+       struct bcm_spi_priv *priv = container_of(filp->private_data, struct bcm_spi_priv, misc);
+       unsigned long int flags;
+
+       if (priv->busy)
+               return -EBUSY;
+
+       priv->busy = true;
+
+       /* Reset circ buffer */
+       priv->read_buf.head = priv->read_buf.tail = 0;
+       priv->write_buf.head = priv->write_buf.tail = 0;
+
+
+       /* Enable irq */
+       spin_lock_irqsave( &priv->irq_lock, flags);
+       if (!atomic_xchg(&priv->irq_enabled, 1))
+               enable_irq(priv->spi->irq);
+
+       spin_unlock_irqrestore( &priv->irq_lock, flags);
+
+       enable_irq_wake(priv->spi->irq);
+
+       filp->private_data = priv;
+#ifdef DEBUG_1HZ_STAT
+       bbd_enable_stat();
+#endif
+       return 0;
+}
+
+static int bcm_spi_release(struct inode *inode, struct file *filp)
+{
+       struct bcm_spi_priv *priv = filp->private_data;
+       unsigned long int flags;
+
+       priv->busy = false;
+
+#ifdef DEBUG_1HZ_STAT
+       bbd_disable_stat();
+#endif
+       /* Disable irq */
+       spin_lock_irqsave( &priv->irq_lock, flags);
+       if (atomic_xchg(&priv->irq_enabled, 0))
+               disable_irq_nosync(priv->spi->irq);
+
+       spin_unlock_irqrestore( &priv->irq_lock, flags);
+
+       disable_irq_wake(priv->spi->irq);
+
+       return 0;
+}
+
+static ssize_t bcm_spi_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos)
+{
+       struct bcm_spi_priv *priv = filp->private_data;
+       struct circ_buf *circ = &priv->read_buf;
+       size_t rd_size=0;
+
+       /* Copy from circ buffer to user
+        * We may require 2 copies from [tail..end] and [end..head]
+        */
+       do {
+               size_t cnt_to_end = CIRC_CNT_TO_END(circ->head, circ->tail, BCM_SPI_READ_BUF_SIZE);
+               size_t copied = min(cnt_to_end, size);
+
+               WARN_ON(copy_to_user(buf + rd_size, (void*) circ->buf + circ->tail, copied));
+               size -= copied;
+               rd_size += copied;
+               circ->tail = (circ->tail + copied) & (BCM_SPI_READ_BUF_SIZE-1);
+
+       } while (size>0 && CIRC_CNT(circ->head, circ->tail, BCM_SPI_READ_BUF_SIZE));
+
+#ifdef DEBUG_1HZ_STAT
+       bbd_update_stat(STAT_RX_LHD, rd_size);
+#endif
+
+       return rd_size;
+}
+
+static ssize_t bcm_spi_write(struct file *filp, const char __user *buf, size_t size, loff_t *ppos)
+{
+       struct bcm_spi_priv *priv = filp->private_data;
+       struct circ_buf *circ = &priv->write_buf;
+       size_t wr_size=0;
+
+       /* Copy from user into circ buffer
+        * We may require 2 copies from [tail..end] and [end..head]
+        */
+       do {
+               size_t space_to_end = CIRC_SPACE_TO_END(circ->head, circ->tail, BCM_SPI_WRITE_BUF_SIZE);
+               size_t copied = min(space_to_end, size);
+
+
+               WARN_ON(copy_from_user((void*) circ->buf + circ->head, buf + wr_size, copied));
+               size -= copied;
+               wr_size += copied;
+               circ->head = (circ->head + copied) & (BCM_SPI_WRITE_BUF_SIZE-1);
+       } while (size>0 && CIRC_SPACE(circ->head, circ->tail, BCM_SPI_WRITE_BUF_SIZE));
+
+       /* kick start rxtx thread */
+       /* we don't want to queue work in suspending and shutdown */
+       if (!atomic_read(&priv->suspending))
+               queue_work(priv->serial_wq, &(priv->rxtx_work) );
+
+#ifdef DEBUG_1HZ_STAT
+       bbd_update_stat(STAT_TX_LHD, wr_size);
+#endif
+       return wr_size;
+}
+
+static unsigned int bcm_spi_poll(struct file *filp, poll_table *wait)
+{
+       struct bcm_spi_priv *priv = filp->private_data;
+       struct circ_buf *rd_circ = &priv->read_buf;
+       struct circ_buf *wr_circ = &priv->write_buf;
+       unsigned int mask = 0;
+
+       poll_wait(filp, &priv->poll_wait, wait);
+
+       if (CIRC_CNT(rd_circ->head, rd_circ->tail, BCM_SPI_READ_BUF_SIZE))
+               mask |= POLLIN;
+
+       if (CIRC_SPACE(wr_circ->head, wr_circ->tail, BCM_SPI_WRITE_BUF_SIZE))
+               mask |= POLLOUT;
+
+       return mask;
+}
+
+
+static const struct file_operations bcm_spi_fops = {
+       .owner          =  THIS_MODULE,
+       .open           =  bcm_spi_open,
+       .release        =  bcm_spi_release,
+       .read           =  bcm_spi_read,
+       .write          =  bcm_spi_write,
+       .poll           =  bcm_spi_poll,
+};
+
+
+
+//--------------------------------------------------------------
+//
+//               Misc. functions
+//
+//--------------------------------------------------------------
+
+/*
+ * bcm4773_hello - wakeup chip by toggling mcu_req while monitoring mcu_resp to check if awake
+ *
+ */
+static bool bcm4773_hello(struct bcm_spi_priv *priv)
+{
+       int count=0, retries=0;
+
+       gpio_set_value(priv->mcu_req, 1);
+       while (!gpio_get_value(priv->mcu_resp)) {
+               if (count++ > 100) {
+                       gpio_set_value(priv->mcu_req, 0);
+                       printk("[SSPBBD] MCU_REQ_RESP timeout. MCU_RESP(gpio%d) not responding to MCU_REQ(gpio%d)\n", 
+                                       priv->mcu_resp, priv->mcu_req);
+                       return false;
+               }
+
+               mdelay(1);
+
+               /*if awake, done */
+               if (gpio_get_value(priv->mcu_resp)) break;
+
+               if (count%20==0 && retries++ < 3) {
+                       gpio_set_value(priv->mcu_req, 0);
+                       mdelay(1);
+                       gpio_set_value(priv->mcu_req, 1);
+                       mdelay(1);
+               }
+       }
+       return true;
+}
+
+/*
+ * bcm4773_bye - set mcu_req low to let chip go to sleep
+ *
+ */
+static void bcm4773_bye(struct bcm_spi_priv *priv)
+{
+       gpio_set_value(priv->mcu_req, 0);
+}
+
+
+static unsigned long init_time = 0;
+static unsigned long clock_get_ms(void)
+{
+       struct timeval t;
+       unsigned long now;
+
+       do_gettimeofday(&t);
+       now = t.tv_usec / 1000 + t.tv_sec * 1000;
+       if ( init_time == 0 )
+               init_time = now;
+
+       return now - init_time;
+}
+
+
+static void pk_log(char* dir, unsigned char* data, int len)
+{
+       char acB[960];
+       char *p = acB;
+       int  the_trans = len;
+       int i,j,n;
+
+       char ic = 'D';
+       char xc = dir[0] == 'r' || dir[0] == 'w' ? 'x':'X';
+
+       if (likely(!ssi_dbg))
+               return;
+
+       n = len;
+       p += snprintf(acB,sizeof(acB),"#%06ld%c %2s,      %5d: ",
+                       clock_get_ms() % 1000000,ic, dir, n);
+
+       for (i=0, n=32; i<len; i=j, n=32)
+       {
+               for(j = i; j < (i + n) && j < len && the_trans != 0; j++,the_trans--) {
+                       p += snprintf(p,sizeof(acB) - (p - acB), "%02X ", data[j]);
+               }
+               pr_info("%s\n",acB);
+               if(j < len) {
+                       p = acB;
+                       if ( the_trans == 0 )  { dir[0] = xc; the_trans--; }
+                       p += snprintf(acB,sizeof(acB),"         %2s              ",dir);
+               }
+       }
+
+       if (i==0)
+               pr_info("%s\n",acB);
+}
+
+
+
+//--------------------------------------------------------------
+//
+//               SSI tx/rx functions
+//
+//--------------------------------------------------------------
+static int bcm_spi_sync(struct bcm_spi_priv *priv, void *tx_buf, void *rx_buf, int len, int bits_per_word)
+{
+       struct spi_message msg;
+       struct spi_transfer xfer;
+       int ret;
+
+       /* Init */
+       spi_message_init(&msg);
+       memset(&xfer, 0, sizeof(xfer));
+       spi_message_add_tail(&xfer, &msg);
+
+       /* Setup */
+       msg.spi = priv->spi;
+       xfer.len = len; 
+       xfer.tx_buf = tx_buf;
+       xfer.rx_buf = rx_buf;
+
+       /* Sync */
+       pk_log("w", (unsigned char *)xfer.tx_buf, len);
+       ret = spi_sync(msg.spi, &msg);
+       pk_log("r", (unsigned char *)xfer.rx_buf, len);
+
+       if (ret)
+               pr_err("[SSPBBD} spi_sync error, return=%d\n", ret);
+
+       return ret;
+}
+
+
+static int bcm_ssi_tx(struct bcm_spi_priv *priv, int length)
+{
+       struct bcm_ssi_tx_frame *tx = priv->tx_buf;
+       struct bcm_ssi_rx_frame *rx = priv->rx_buf;
+       int bits_per_word = (length >= 255) ? CONFIG_SPI_DMA_BITS_PER_WORD : 8;
+       int ret;
+
+       tx->cmd = SSI_WRITE_HD;
+       ret = bcm_spi_sync(priv, tx, rx, length+1, bits_per_word); /*1 for tx cmd */
+
+       return ret;
+}
+
+static int bcm_ssi_rx( struct bcm_spi_priv *priv, size_t *length)
+{
+       struct bcm_ssi_tx_frame *tx = priv->tx_buf;
+       struct bcm_ssi_rx_frame *rx = priv->rx_buf;
+
+       memset(tx, 0, MAX_SPI_FRAME_LEN);
+       tx->cmd = SSI_READ_HD;
+       rx->status = 0;
+
+       if (bcm_spi_sync(priv, tx, rx, 2, 8))  /* 2 for rx status + len */
+               return -1;
+
+       /* Check Sanity */
+       if (rx->status) {
+               pr_err("[SSPBBD] spi_sync error, status = 0x%02X\n", rx->status);
+               return -1;
+       }
+
+       if (rx->len == 0)
+       {
+               rx->len = MAX_SPI_FRAME_LEN;
+               pr_err("[SSPBBD] rx->len is still read to 0. set MAX_SPI_FRAME_LEN\n");
+       }
+
+       /* limit max payload to 254 because of exynos3 bug */
+       *length = min((unsigned char)(MAX_SPI_FRAME_LEN-2), rx->len);
+       if (bcm_spi_sync(priv, tx, rx, *length+2, 8)) /* 2 for rx status + len */
+               return -1;
+
+       /* Check Sanity */
+       if (rx->status) {
+               pr_err("[SSPBBD] spi_sync error, status = 0x%02X\n", rx->status);
+               return -1;
+       }
+
+       if (rx->len < *length) {
+               *length = rx->len; /* workaround */
+       }
+
+#ifdef DEBUG_1HZ_STAT
+       bbd_update_stat(STAT_RX_SSI, *length);
+#endif
+       return 0;
+}
+
+void bcm_on_packet_recieved(void *_priv, unsigned char *data, unsigned int size)
+{
+       struct bcm_spi_priv *priv = (struct bcm_spi_priv *)_priv;
+       struct circ_buf *rd_circ = &priv->read_buf;
+       size_t written=0, avail = size;
+
+       /* Copy into circ buffer */
+       mutex_lock(&priv->rlock);
+       do {
+               size_t space_to_end = CIRC_SPACE_TO_END(rd_circ->head, rd_circ->tail, BCM_SPI_READ_BUF_SIZE);
+               size_t copied = min(space_to_end, avail);
+
+               memcpy((void*) rd_circ->buf + rd_circ->head, data + written, copied);
+               avail -= copied;
+               written += copied;
+               rd_circ->head = (rd_circ->head + copied) & (BCM_SPI_READ_BUF_SIZE-1);
+
+       } while (avail>0 && CIRC_SPACE(rd_circ->head, rd_circ->tail, BCM_SPI_READ_BUF_SIZE));
+       mutex_unlock(&priv->rlock);
+       wake_up(&priv->poll_wait);
+
+       if (avail>0)
+               pr_err("[SSPBBD]: input overrun error by %zd bytes!\n", avail);
+}
+
+static void bcm_rxtx_work( struct work_struct *work )
+{
+#ifdef DEBUG_1HZ_STAT
+       u64 ts_rx_start = 0;
+       u64 ts_rx_end = 0;
+#endif
+       struct bcm_spi_priv *priv = container_of(work, struct bcm_spi_priv, rxtx_work);
+       struct circ_buf *wr_circ = &priv->write_buf;
+
+       if (!bcm4773_hello(priv)) {
+               pr_err("[SSPBBD]: %s timeout!!\n", __func__);
+               return;
+       }
+
+       do {
+               /* Read first */
+               if (gpio_get_value(priv->host_req)) {
+                       size_t avail;
+#ifdef DEBUG_1HZ_STAT
+                       struct timespec ts;
+                       if (stat1hz.ts_irq) {
+                               ts = ktime_to_timespec(ktime_get_boottime());
+                               ts_rx_start = ts.tv_sec * 1000000000ULL
+                                       + ts.tv_nsec;
+                       }
+#endif
+                       /* Receive SSI frame */
+                       if (bcm_ssi_rx(priv, &avail))
+                               break;
+
+#ifdef DEBUG_1HZ_STAT
+                       bbd_update_stat(STAT_RX_SSI, avail);
+                       if (ts_rx_start && !gpio_get_value(priv->host_req)) {
+                               ts = ktime_to_timespec(ktime_get_boottime());
+                               ts_rx_end = ts.tv_sec * 1000000000ULL
+                                       + ts.tv_nsec;
+                       }
+#endif
+                       /* Call BBD */
+                       bbd_parse_asic_data(priv->rx_buf->data, avail, NULL, priv);
+               }
+
+               /* Next, write */
+               if (CIRC_CNT(wr_circ->head, wr_circ->tail, BCM_SPI_WRITE_BUF_SIZE)) {
+                       size_t written=0, avail=0;
+
+                       avail = CIRC_CNT(wr_circ->head, wr_circ->tail, BCM_SPI_WRITE_BUF_SIZE);
+                       /* For big packet, we should align xfer size to DMA word size and burst size.
+                        * That is, SSI payload + one byte command should be multiple of (DMA word size * burst size)
+                        */
+                       if (avail > MAX_SPI_FRAME_LEN - 1)
+                               avail = MAX_SPI_FRAME_LEN - 1;
+
+                       /* Copy from wr_circ the data */
+                       do {
+                               size_t cnt_to_end = CIRC_CNT_TO_END(wr_circ->head, wr_circ->tail, BCM_SPI_WRITE_BUF_SIZE);
+                               size_t copied = min(cnt_to_end, avail);
+
+                               memcpy(priv->tx_buf->data + written, wr_circ->buf + wr_circ->tail, copied);
+                               avail -= copied;
+                               written += copied;
+                               wr_circ->tail = (wr_circ->tail + copied) & (BCM_SPI_WRITE_BUF_SIZE-1);
+                       } while (avail>0);
+
+                       /* Transmit SSI frame */
+                       if (bcm_ssi_tx(priv, written))
+                               break;
+
+                       wake_up(&priv->poll_wait);
+#ifdef DEBUG_1HZ_STAT
+                       bbd_update_stat(STAT_TX_SSI, written);
+#endif
+               }
+
+       } while (gpio_get_value(priv->host_req) || CIRC_CNT(wr_circ->head, wr_circ->tail, BCM_SPI_WRITE_BUF_SIZE));
+
+       bcm4773_bye(priv);
+
+       /* Enable irq */
+       {
+               unsigned long int flags;
+               spin_lock_irqsave( &priv->irq_lock, flags);
+
+               /* we dont' want to enable irq when going to suspending */
+               if (!atomic_read(&priv->suspending))
+                       if (!atomic_xchg(&priv->irq_enabled, 1))
+                               enable_irq(priv->spi->irq);
+
+               spin_unlock_irqrestore( &priv->irq_lock, flags);
+       }
+
+#ifdef DEBUG_1HZ_STAT
+       if (stat1hz.ts_irq && ts_rx_start && ts_rx_end) {
+               u64 lat = ts_rx_start - stat1hz.ts_irq;
+               u64 dur = ts_rx_end - ts_rx_start;
+               stat1hz.min_rx_lat = (lat < stat1hz.min_rx_lat) ?
+                       lat : stat1hz.min_rx_lat;
+               stat1hz.max_rx_lat = (lat > stat1hz.max_rx_lat) ?
+                       lat : stat1hz.max_rx_lat;
+               stat1hz.min_rx_dur = (dur < stat1hz.min_rx_dur) ?
+                       dur : stat1hz.min_rx_dur;
+               stat1hz.max_rx_dur = (dur > stat1hz.max_rx_dur) ?
+                       dur : stat1hz.max_rx_dur;
+               stat1hz.ts_irq = 0;
+       }
+#endif
+}
+
+
+//--------------------------------------------------------------
+//
+//               IRQ Handler
+//
+//--------------------------------------------------------------
+static irqreturn_t bcm_irq_handler(int irq, void *pdata)
+{
+       struct bcm_spi_priv *priv = (struct bcm_spi_priv *) pdata;
+
+       if (!gpio_get_value(priv->host_req))
+               return IRQ_HANDLED;
+#ifdef DEBUG_1HZ_STAT
+       {
+               struct timespec ts;
+               ts = ktime_to_timespec(ktime_get_boottime());
+               stat1hz.ts_irq = ts.tv_sec * 1000000000ULL + ts.tv_nsec;
+       }
+#endif
+       /* Disable irq */
+       spin_lock(&priv->irq_lock);
+       if (atomic_xchg(&priv->irq_enabled, 0))
+               disable_irq_nosync(priv->spi->irq);
+
+       spin_unlock(&priv->irq_lock);
+
+       /* we don't want to queue work in suspending and shutdown */
+       if (!atomic_read(&priv->suspending))
+               queue_work(priv->serial_wq, &priv->rxtx_work);
+
+       wake_lock_timeout(&priv->bcm_wake_lock, HZ/2);
+       return IRQ_HANDLED;
+}
+
+
+//--------------------------------------------------------------
+//
+//               SPI driver operations
+//
+//--------------------------------------------------------------
+
+static void bcm_spi_shutdown(struct spi_device *spi)
+{
+       struct bcm_spi_priv *priv = (struct bcm_spi_priv*) spi_get_drvdata(spi);
+       unsigned long int flags;
+
+       printk("[SSPBBD]: %s ++ \n", __func__);
+
+       atomic_set(&priv->suspending, 1);
+
+       /* Disable irq */
+       spin_lock_irqsave( &priv->irq_lock, flags);
+       if (atomic_xchg(&priv->irq_enabled, 0))
+               disable_irq_nosync(spi->irq);
+
+       spin_unlock_irqrestore( &priv->irq_lock, flags);
+
+       printk("[SSPBBD]: %s ** \n", __func__);
+
+       flush_workqueue(priv->serial_wq );
+       destroy_workqueue( priv->serial_wq );
+
+       printk("[SSPBBD]: %s -- \n", __func__);
+}
+
+static int bcm_spi_probe(struct spi_device *spi)
+{
+       int host_req, mcu_req, mcu_resp;
+       struct bcm_spi_priv *priv;
+       int ret;
+
+       /* Check GPIO# */
+#ifndef CONFIG_OF
+       pr_err("[SSPBBD]: Check platform_data for bcm device\n");
+#else
+       if (!spi->dev.of_node) {
+               pr_err("[SSPBBD]: Failed to find of_node\n");
+               goto err_exit;
+       }
+#endif
+
+       host_req = of_get_named_gpio(spi->dev.of_node, "ssp-irq", 0);
+       mcu_req  = of_get_named_gpio(spi->dev.of_node, "ssp-mcu-req", 0);
+       mcu_resp = of_get_named_gpio(spi->dev.of_node, "ssp-mcu-resp", 0);
+
+       pr_info("[SSPBBD] ssp-host-req=%d, ssp-mcu_req=%d, ssp-mcu-resp=%d\n", host_req, mcu_req, mcu_resp);
+       if (host_req<0 || mcu_req<0 || mcu_resp<0) {
+               pr_err("[SSPBBD]: GPIO value not correct\n");
+               goto err_exit;
+       }
+
+       /* Check IRQ# */
+       spi->irq = gpio_to_irq(host_req);
+       if (spi->irq < 0) {
+               pr_err("[SSPBBD]: irq=%d for host_req=%d not correct\n", spi->irq, host_req);
+               goto err_exit;
+       }
+
+       /* Config GPIO */
+       ret = gpio_request(mcu_req, "MCU REQ");
+       if (ret){
+               pr_err("[SSPBBD]: failed to request MCU REQ, ret:%d", ret);
+               goto err_exit;
+       }
+       ret = gpio_direction_output(mcu_req, 0);
+       if (ret) {
+               pr_err("[SSPBBD]: failed set MCU REQ as input mode, ret:%d", ret);
+               goto err_exit;
+       }
+       ret = gpio_request(mcu_resp, "MCU RESP");
+       if (ret){
+               pr_err("[SSPBBD]: failed to request MCU RESP, ret:%d", ret);
+               goto err_exit;
+       }
+       ret = gpio_direction_input(mcu_resp);
+       if (ret) {
+               pr_err("[SSPBBD]: failed set MCU RESP as input mode, ret:%d", ret);
+               goto err_exit;
+       }
+
+       /* Alloc everything */
+       priv = (struct bcm_spi_priv*) kmalloc(sizeof(*priv), GFP_KERNEL);
+       if (!priv) {
+               pr_err("[SSPBBD]: Failed to allocate memory for the BCM SPI driver\n");
+               goto err_exit;
+       }
+
+       memset(priv, 0, sizeof(*priv));
+
+       priv->spi = spi;
+
+       priv->tx_buf = kmalloc(sizeof(struct bcm_ssi_tx_frame), GFP_KERNEL);
+       priv->rx_buf = kmalloc(sizeof(struct bcm_ssi_rx_frame), GFP_KERNEL);
+       if (!priv->tx_buf || !priv->rx_buf) {
+               pr_err("[SSPBBD]: Failed to allocate xfer buffer. tx_buf=%p, rx_buf=%p\n",
+                               priv->tx_buf, priv->rx_buf);
+               goto free_mem;
+       }
+
+       priv->clk = devm_clk_get(&spi->dev, "xtal");
+       if (IS_ERR(priv->clk)) {
+               pr_err("[SSPBBD] failed to get clock\n");
+               goto free_mem;
+       }
+
+       ret = clk_prepare_enable(priv->clk);
+       if (ret) {
+               goto free_mem;
+               pr_err("[SSPBBD] failed to enable clock\n");
+       }
+
+       priv->serial_wq = alloc_workqueue("bcm4773_wq", WQ_HIGHPRI|WQ_UNBOUND|WQ_MEM_RECLAIM, 1);
+       if (!priv->serial_wq) {
+               pr_err("[SSPBBD]: Failed to allocate workqueue\n");
+               goto free_mem;
+       }
+
+       /* Request IRQ */
+       ret = request_irq(spi->irq, bcm_irq_handler, IRQF_TRIGGER_HIGH, "ttyBCM", priv);
+       if (ret) {
+               pr_err("[SSPBBD]: Failed to register BCM4774 SPI TTY IRQ %d.\n",spi->irq);
+               goto free_wq;
+       }
+
+       disable_irq(spi->irq);
+
+       pr_notice("[SSPBBD]: Probe OK. ssp-host-req=%d, irq=%d, priv=0x%p\n", host_req, spi->irq, priv);
+
+       /* Register misc device */
+       priv->misc.minor = MISC_DYNAMIC_MINOR;
+       priv->misc.name = "ttyBCM";
+       priv->misc.fops = &bcm_spi_fops;
+
+       ret = misc_register(&priv->misc);
+       if (ret) {
+               pr_err("[SSPBBD]: Failed to register bcm_gps_spi's misc dev. err=%d\n", ret);
+               goto free_irq;
+       }
+
+       /* Set driver data */
+       spi_set_drvdata(spi, priv);
+
+       /* Init - miscdev stuff */
+       init_waitqueue_head(&priv->poll_wait);
+       priv->read_buf.buf = priv->_read_buf;
+       priv->write_buf.buf = priv->_write_buf;
+       mutex_init(&priv->rlock);
+       mutex_init(&priv->wlock);
+       priv->busy = false;
+
+       /* Init - work */
+       INIT_WORK(&priv->rxtx_work, bcm_rxtx_work);
+
+       /* Init - irq stuff */
+       spin_lock_init(&priv->irq_lock);
+       atomic_set(&priv->irq_enabled, 0);
+       atomic_set(&priv->suspending, 0);
+
+       /* Init - gpios */
+       priv->host_req = host_req;
+       priv->mcu_req  = mcu_req;
+       priv->mcu_resp = mcu_resp;
+
+       /* Init - etc */
+       wake_lock_init(&priv->bcm_wake_lock, WAKE_LOCK_SUSPEND, "bcm_spi_wake_lock");
+
+       g_bcm_gps = priv;
+       /* Init BBD & SSP */
+       bbd_init(&spi->dev);
+
+       return 0;
+
+free_irq:
+       if (spi->irq)
+               free_irq( spi->irq, priv );
+free_wq:
+       if (priv->serial_wq)
+               destroy_workqueue(priv->serial_wq);
+free_mem:
+       if (priv->tx_buf)
+               kfree(priv->tx_buf);
+       if (priv->rx_buf)
+               kfree(priv->rx_buf);
+       if (priv)
+               kfree(priv);
+err_exit:
+       return -ENODEV;
+}
+
+
+static int bcm_spi_remove(struct spi_device *spi)
+{
+       struct bcm_spi_priv *priv = (struct bcm_spi_priv*) spi_get_drvdata(spi);
+       unsigned long int flags;
+
+       pr_notice("[SSPBBD]:  %s : called\n", __func__);
+
+       atomic_set(&priv->suspending, 1);
+
+       /* Disable irq */
+       spin_lock_irqsave(&priv->irq_lock, flags);
+       if (atomic_xchg(&priv->irq_enabled, 0))
+               disable_irq_nosync(spi->irq);
+
+       spin_unlock_irqrestore( &priv->irq_lock, flags);
+
+       /* Flush work */
+       flush_workqueue(priv->serial_wq);
+       destroy_workqueue(priv->serial_wq);
+
+       /* Free everything */
+       free_irq( spi->irq, priv );
+       kfree(priv->tx_buf);
+       kfree(priv->rx_buf);
+       kfree( priv );
+
+       g_bcm_gps = NULL;
+
+       return 0;
+}
+
+void bcm4773_debug_info(void)
+{
+       int pin_ttyBCM, pin_MCU_REQ, pin_MCU_RESP;
+       int irq_enabled, irq_count;
+
+       pin_ttyBCM = gpio_get_value(g_bcm_gps->host_req);
+       pin_MCU_REQ = gpio_get_value(g_bcm_gps->mcu_req);
+       pin_MCU_RESP = gpio_get_value(g_bcm_gps->mcu_resp);
+
+       irq_enabled = atomic_read(&g_bcm_gps->irq_enabled);
+       irq_count = kstat_irqs_cpu(g_bcm_gps->spi->irq, 0);
+
+       printk("[SSPBBD]: %s pin_ttyBCM:%d, pin_MCU_REQ:%d, pin_MCU_RESP:%d\n",
+                       __func__, pin_ttyBCM, pin_MCU_REQ, pin_MCU_RESP);
+       printk("[SSPBBD]: %s irq_enabled:%d, irq_count:%d\n",
+                       __func__, irq_enabled, irq_count);
+}
+
+
+static const struct spi_device_id bcm_spi_id[] = {
+       {"ssp", 0},
+       {}
+};
+MODULE_DEVICE_TABLE(spi, bcm_spi_id);
+
+#ifdef CONFIG_OF
+static struct of_device_id match_table[] = {
+       { .compatible = "ssp,BCM4773",},
+       { .compatible = "ssp,BCM4774",},
+       {},
+};
+#endif
+
+#ifdef CONFIG_PM_SLEEP
+static int bcm_spi_suspend(struct device *dev)
+{
+       struct spi_device *spi = to_spi_device(dev);
+       struct bcm_spi_priv *priv = (struct bcm_spi_priv*) spi_get_drvdata(spi);
+       unsigned long int flags;
+
+       printk("[SSPBBD]: %s ++ \n", __func__);
+
+       atomic_set(&priv->suspending, 1);
+
+       /* Disable irq */
+       spin_lock_irqsave(&priv->irq_lock, flags);
+       if (atomic_xchg(&priv->irq_enabled, 0))
+               disable_irq_nosync(spi->irq);
+
+       spin_unlock_irqrestore(&priv->irq_lock, flags);
+
+       flush_workqueue(priv->serial_wq);
+
+       return 0;
+}
+
+static int bcm_spi_resume(struct device *dev)
+{
+       struct spi_device *spi = to_spi_device(dev);
+       struct bcm_spi_priv *priv = (struct bcm_spi_priv*) spi_get_drvdata(spi);
+       unsigned long int flags;
+
+       pr_info("[SSPBBD]: %s ++ \n", __func__);
+       atomic_set(&priv->suspending, 0);
+
+       /* Enable irq */
+       spin_lock_irqsave(&priv->irq_lock, flags);
+       if (!atomic_xchg(&priv->irq_enabled, 1))
+               enable_irq(spi->irq);
+
+       spin_unlock_irqrestore( &priv->irq_lock, flags);
+
+       pr_info("[SSPBBD]: %s -- \n", __func__);
+       return 0;
+}
+
+static const struct dev_pm_ops bcm_spi_pm = {
+       SET_SYSTEM_SLEEP_PM_OPS(bcm_spi_suspend, bcm_spi_resume)
+};
+#endif /* CONFIG_PM_SLEEP */
+
+static struct spi_driver bcm_spi_driver =
+{
+       .id_table = bcm_spi_id,
+       .probe = bcm_spi_probe,
+       .remove = bcm_spi_remove,
+       .shutdown = bcm_spi_shutdown,
+       .driver = {
+               .name = "ssp",
+               .owner = THIS_MODULE,
+               .pm = &bcm_spi_pm,
+#ifdef CONFIG_OF
+               .of_match_table = match_table
+#endif
+       },
+};
+
+
+module_spi_driver(bcm_spi_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("BCM SPI/SSI Driver");