summaryrefslogtreecommitdiff
path: root/drivers/sensors/brcm
diff options
context:
space:
mode:
authorHoegeun Kwon <hoegeun.kwon@samsung.com>2016-08-18 17:46:19 +0900
committerSeung-Woo Kim <sw0312.kim@samsung.com>2016-12-14 13:52:39 +0900
commit73cc765dc643ceb34b1d11b5c7894f046479adb0 (patch)
treed2f9f34e1c97841f0552ce395f2ff4403809e0bd /drivers/sensors/brcm
parentdaa6518f9a6205b9565257a8ac94c6257feaac7d (diff)
sensors: brcm: bbdpl2: add new version (v2) of the BCM4773 sensorhub
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>
Diffstat (limited to 'drivers/sensors/brcm')
-rw-r--r--drivers/sensors/brcm/Kconfig26
-rw-r--r--drivers/sensors/brcm/Makefile5
-rw-r--r--drivers/sensors/brcm/bbdpl2/Kconfig28
-rw-r--r--drivers/sensors/brcm/bbdpl2/Makefile4
-rw-r--r--drivers/sensors/brcm/bbdpl2/bbd.c997
-rw-r--r--drivers/sensors/brcm/bbdpl2/bbd.h109
-rw-r--r--drivers/sensors/brcm/bbdpl2/bbd_rpc_lh.c712
-rw-r--r--drivers/sensors/brcm/bbdpl2/bcm_dbg.c33
-rw-r--r--drivers/sensors/brcm/bbdpl2/bcm_gps_i2c.c741
-rw-r--r--drivers/sensors/brcm/bbdpl2/bcm_gps_i2c.h22
-rw-r--r--drivers/sensors/brcm/bbdpl2/bcm_gps_spi.c968
11 files changed, 3641 insertions, 4 deletions
diff --git a/drivers/sensors/brcm/Kconfig b/drivers/sensors/brcm/Kconfig
index c9829e298828..2e1cc096fdd1 100644
--- a/drivers/sensors/brcm/Kconfig
+++ b/drivers/sensors/brcm/Kconfig
@@ -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"
diff --git a/drivers/sensors/brcm/Makefile b/drivers/sensors/brcm/Makefile
index 71843a1eebe9..b965e2119334 100644
--- a/drivers/sensors/brcm/Makefile
+++ b/drivers/sensors/brcm/Makefile
@@ -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
index 000000000000..5697dde7f2fd
--- /dev/null
+++ b/drivers/sensors/brcm/bbdpl2/Kconfig
@@ -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
index 000000000000..feeb3f5445ad
--- /dev/null
+++ b/drivers/sensors/brcm/bbdpl2/Makefile
@@ -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
index 000000000000..ee98fb7ae030
--- /dev/null
+++ b/drivers/sensors/brcm/bbdpl2/bbd.c
@@ -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
index 000000000000..c632390c3baa
--- /dev/null
+++ b/drivers/sensors/brcm/bbdpl2/bbd.h
@@ -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
index 000000000000..5a49fc39350f
--- /dev/null
+++ b/drivers/sensors/brcm/bbdpl2/bbd_rpc_lh.c
@@ -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
index 000000000000..97ef8eda8fd7
--- /dev/null
+++ b/drivers/sensors/brcm/bbdpl2/bcm_dbg.c
@@ -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
index 000000000000..08a8f9453192
--- /dev/null
+++ b/drivers/sensors/brcm/bbdpl2/bcm_gps_i2c.c
@@ -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
index 000000000000..08c40ca1e854
--- /dev/null
+++ b/drivers/sensors/brcm/bbdpl2/bcm_gps_i2c.h
@@ -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
index 000000000000..8434aa157e11
--- /dev/null
+++ b/drivers/sensors/brcm/bbdpl2/bcm_gps_spi.c
@@ -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");