ASR_BASE

Change-Id: Icf3719cc0afe3eeb3edc7fa80a2eb5199ca9dda1
diff --git a/marvell/linux/drivers/char/sulog.c b/marvell/linux/drivers/char/sulog.c
new file mode 100644
index 0000000..6a1334c
--- /dev/null
+++ b/marvell/linux/drivers/char/sulog.c
@@ -0,0 +1,893 @@
+/*
+ * Marvell sulog driver
+ *
+ * Tzviel Lemberger <tzviel@marvell.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ * (c) 2010
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/miscdevice.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+#include <linux/vmalloc.h>
+#include <linux/workqueue.h>
+#include <asm/io.h>
+
+
+#include <linux/debugfs.h>
+#include <linux/usb.h>
+#include <linux/timer.h>
+
+/* TODO: find a proper solution to the 2 functions included from pxa182x_serial_debug.h */
+extern struct debug_serial_direct_callbacks *pxa_register_debug_serial_streaming(void);
+extern void pxa_unregister_debug_serial_streaming(void);
+extern struct debug_serial_direct_callbacks *pxa_register_diag_serial_streaming(void);
+extern void pxa_unregister_diag_serial_streaming(void);
+
+//#include "pxa182x_serial_debug.h"
+
+/*
+ * NOTE: send_buf_func called from an atomic context (timer callback).
+ * You are responsible to check wether the send_buf_func can be called
+ * from atomic context.
+ */
+static int (*send_buf_func)(const unsigned char *buf, int count);
+
+/* Sulog macros */
+#define PR_ERR(fmt, ...)					\
+	pr_err("misc sulog: %s(%d): "				\
+		fmt, __func__, __LINE__ , ##__VA_ARGS__)
+
+#define DEV_ERR(dev, fmt, arg...)				\
+	pr_err("%s %s: %s(%d): " fmt, dev_driver_string(dev),	\
+			dev_name(dev), __func__, __LINE__, ## arg)
+#define DEV_INFO(dev, fmt, arg...)				\
+	pr_info("%s %s: %s(%d): " fmt, dev_driver_string(dev),	\
+			dev_name(dev), __func__, __LINE__, ## arg)
+/*#define SULOG_DBG 1*/
+#ifdef SULOG_DBG
+#define DEV_DBG(dev, fmt, arg...)				\
+	pr_debug("%s %s: %s(%d): " fmt, dev_driver_string(dev),	\
+			dev_name(dev), __func__, __LINE__, ## arg)
+#else
+#define DEV_DBG(dev, fmt, arg...)
+#endif
+
+#define SULOG_ENABLE	1
+#define SULOG_ENABLE2	2	/* sulog thru diag port */
+#define SULOG_DISABLE	0
+/*Default AP DDR memory allocation */
+#define AP_DDR_DEFAULT_SIZE		0x7f000
+#define NUM_OF_BUFFERS	2
+/*Default high water mark level */
+#define AP_HIGHWATERMARK	0x1000
+/*Comm maximum DDR size*/
+#define CP_MAX_DDR_SIZE	0x100000
+
+/*Register offsets*/
+#define RIPC_STATUS_REG	0x0
+#define RIPC_APPS_INT_REG	0x4
+#define RIPC_SG_INT_REG	0x8
+#define RIPC_GB_INT_REG	0xC
+#define RIPC_APPS_INFO_REG	0x14
+#define RIPC_SG_INFO_REG	0x18
+#define RIPC_GB_INFO_REG	0x1C
+/*Two base addresses for RIPC - registers and Clk*/
+#define RIPC_BASE_ADDRESSES	0x2
+#define RIPC_APPS_CLK_REG	0x0
+
+#define RIPC_CLR_INTRRUPT_BIT	0x1
+
+#define TARGET_SD		0x0
+#define TARGET_USB		0x1
+
+/* Used to ioremap large enough space
+   in case of address outside of expected CP memory */
+#define COMM_DSP_MAX_BUFFER_SIZE	0x200000
+/* Used to test if requseted buffer size is not too large */
+#define COMM_DSP_DOUBLE_BUFFER_SIZE	0x100000
+
+/*Static variables*/
+static struct sulog_struct *sulog_str;
+static int sulog_thru_diag;
+volatile static int glb_cnt;
+/*General Structures*/
+struct sulog_resource {
+	unsigned int *ripc_base_address;
+	unsigned char *comm_strt_addr;
+	unsigned int comm_addr_offst;
+	unsigned char *comm_base_addr;
+	unsigned int ripc_size;
+	int sulog_irq_resource;
+	char sulog_irq_name[20];
+	struct resource *resource;
+	unsigned int remapped;
+};
+
+struct sulog_app_mem {
+	unsigned int *sulog_app_ptr;
+	unsigned int *sulog_app_buf[NUM_OF_BUFFERS];
+	unsigned int sulog_allocbuf_size;
+	unsigned int app_buff_size;
+	unsigned int high_watermark;
+	unsigned int sulog_target_type;
+};
+
+struct sulog_statistic {
+	unsigned int irq_count;
+	unsigned int tot_data_sum;
+	unsigned int packets_pass;
+	unsigned int packets_dropped;
+};
+
+struct sulog_struct {
+	struct device *dev;
+	struct sulog_resource res[RIPC_BASE_ADDRESSES];
+	struct sulog_app_mem app_mem;
+	unsigned int sulog_driver_status;
+	struct tasklet_struct sulog_copy_tasklet;
+	struct workqueue_struct *sulog_workqueue_ptr;
+	struct work_struct sulog_workstruct;
+	struct sulog_statistic sulog_statistic;
+	wait_queue_head_t read_queue;
+};
+
+/*Function declerations*/
+static void sulog_comm_map(struct work_struct *work);
+static void sulog_copy_tasklet(unsigned long data);
+static int sulog_driver_enable(void);
+static int sulog_driver_disable(void);
+static int sulog_change_status(unsigned int input);
+static ssize_t sulog_driver_show(struct device *dev,
+				 struct device_attribute *attr,
+				 char *buf);
+static ssize_t sulog_driver_store(struct device *dev,
+				  struct device_attribute *attr,
+				  const char *buff, size_t count);
+static ssize_t sulog_watermark_show(struct device *dev,
+				    struct device_attribute *attr,
+				    char *buf);
+static ssize_t sulog_watermark_store(struct device *dev,
+				     struct device_attribute *attr,
+				     const char *buff, size_t count);
+static ssize_t sulog_buff_size_show(struct device *dev,
+				    struct device_attribute *attr,
+				    char *buf);
+static ssize_t sulog_buff_size_store(struct device *dev,
+				     struct device_attribute *attr,
+				     const char *buff, size_t count);
+static ssize_t sulog_target_type_show(struct device *dev,
+				    struct device_attribute *attr,
+				    char *buf);
+static ssize_t sulog_target_type_store(struct device *dev,
+				     struct device_attribute *attr,
+				     const char *buff, size_t count);
+static ssize_t sulog_statistics_show(struct device *dev,
+				 struct device_attribute *attr,
+				 char *buf);
+static ssize_t sulog_statistics_store(struct device *dev,
+				  struct device_attribute *attr,
+				  const char *buff, size_t count);
+
+static irqreturn_t sulog_irq_handler(int this_irq, void *dev_id);
+static int sulog_open(struct inode *inode, struct file *file);
+static ssize_t sulog_read(struct file *filp, char __user *buf,
+			  size_t count, loff_t *unused);
+
+/*Static structures*/
+static const struct file_operations sulog_fops = {
+	.owner = THIS_MODULE,
+	.open = sulog_open,
+	.read = sulog_read,
+};
+
+static struct miscdevice sulog_miscdev = {
+	.minor = MISC_DYNAMIC_MINOR,
+	.name = "sulog_driver",
+	.fops = &sulog_fops,
+};
+
+static DEVICE_ATTR(status, 0644, sulog_driver_show, sulog_driver_store);
+static DEVICE_ATTR(watermark, 0644, sulog_watermark_show,
+		   sulog_watermark_store);
+static DEVICE_ATTR(buff_size, 0644, sulog_buff_size_show,
+		   sulog_buff_size_store);
+static DEVICE_ATTR(target_type, 0644, sulog_target_type_show,
+		   sulog_target_type_store);
+static DEVICE_ATTR(statistics, 0644, sulog_statistics_show,
+		   sulog_statistics_store);
+
+static const struct device_attribute *dev_attr_arr[] = {
+	&dev_attr_status,
+	&dev_attr_watermark,
+	&dev_attr_buff_size,
+	&dev_attr_target_type,
+	&dev_attr_statistics,
+};
+
+/*Static functions*/
+static void sulog_comm_map(struct work_struct *work)
+{
+	/* The workqueue is responsible to deliver the tasklet with a valid address to work with.
+	 * if physical address is inside kernel memory - translate it to a virtual address and continue.
+	 * if physical address is outside of kernel memory - remap it first. */
+	if (sulog_str->res[0].comm_strt_addr && sulog_str->res[0].remapped)
+		iounmap(sulog_str->res[0].comm_strt_addr);
+
+	if (pfn_valid(__phys_to_pfn((unsigned int)sulog_str->res[0].comm_base_addr))) {
+		/* Physical address inside kernel memory */
+		sulog_str->res[0].remapped = 0;
+		sulog_str->res[0].comm_strt_addr =
+				phys_to_virt((unsigned int)sulog_str->res[0].comm_base_addr);
+	} else {
+		/* Physical address outside of kernel memory */
+		sulog_str->res[0].remapped = 1;
+		sulog_str->res[0].comm_strt_addr =
+		    ioremap((unsigned int)sulog_str->res[0].comm_base_addr,
+				COMM_DSP_MAX_BUFFER_SIZE);
+	}
+
+	DEV_DBG(sulog_str->dev, "Comm memory remapped, triggering tasklet\n");
+	tasklet_schedule(&sulog_str->sulog_copy_tasklet);
+	return;
+}
+
+static void sulog_copy_tasklet(unsigned long data)
+{
+	unsigned int data_size;
+	unsigned char *resource_p;
+
+
+	DEV_DBG(sulog_str->dev,
+		"Start copying information from SHMEM to Ap DDR\n");
+	data_size = (unsigned int) sulog_str->res[0].ripc_size;
+	resource_p = (sulog_str->res[0].comm_strt_addr +
+		 sulog_str->res[0].comm_addr_offst);
+
+	if (sulog_str->app_mem.sulog_target_type == TARGET_USB) {
+		/*Output is usb: Copying data from SHMEM to kernel memory */
+		if (!send_buf_func(resource_p, data_size))
+			sulog_str->sulog_statistic.packets_pass++;
+		else
+			sulog_str->sulog_statistic.packets_dropped++;
+	} else { /* TARGET SD */
+		/* Output is sdcard: Copying data from SHMEM to AP DDR*/
+		if (unlikely
+		    ((sulog_str->app_mem.app_buff_size + data_size) >
+		     sulog_str->app_mem.sulog_allocbuf_size)) {
+			/*We will exceed the maximum allocation size */
+			DEV_INFO(sulog_str->dev,
+				 "Data exceed maximum size, possible data lost\n");
+			/*Allocate all remaining space for copy */
+			data_size =
+			    sulog_str->app_mem.sulog_allocbuf_size -
+			    sulog_str->app_mem.app_buff_size;
+			/* We missed some data. mark as lost packet */
+			sulog_str->sulog_statistic.packets_dropped++;
+		}
+		memcpy((unsigned char *)
+			((unsigned int)sulog_str->app_mem.sulog_app_ptr +
+			 (unsigned int)sulog_str->app_mem.app_buff_size),
+			resource_p,
+			data_size);
+		sulog_str->app_mem.app_buff_size += data_size;
+		if (sulog_str->app_mem.app_buff_size >
+		    sulog_str->app_mem.high_watermark) {
+			/*Allow user space to access the data */
+			disable_irq(sulog_str->res[0].sulog_irq_resource);
+			DEV_DBG(sulog_str->dev, "waking up read to user space\n\n");
+			wake_up(&sulog_str->read_queue);
+		}
+	}
+	return;
+}
+
+static int sulog_driver_enable(void)
+{
+	int i;
+
+	struct debug_serial_direct_callbacks *callback_ptr;
+	if (sulog_str->app_mem.sulog_target_type == TARGET_SD) {
+		/*We'll allocate double buffer*/
+		for (i = 0; i < NUM_OF_BUFFERS; i++) {
+			sulog_str->app_mem.sulog_app_buf[i] =
+				vmalloc((unsigned long)
+					sulog_str->app_mem.sulog_allocbuf_size);
+			if (sulog_str->app_mem.sulog_app_buf[i] == NULL) {
+				PR_ERR("Failed to allocate memory!\n");
+				return -ENOMEM;
+			}
+		}
+		sulog_str->app_mem.sulog_app_ptr =
+			sulog_str->app_mem.sulog_app_buf[glb_cnt];
+	}
+
+	/*Set RIPC clk to 1 - JIRA NEZHA3-90*/
+	__raw_writel(0x1, sulog_str->res[1].ripc_base_address);
+	if (sulog_str->app_mem.sulog_target_type == TARGET_USB) {
+		if (sulog_thru_diag) {
+			pr_info("Sulog is sent through diag port!\n");
+			callback_ptr = pxa_register_diag_serial_streaming();
+		} else {
+			callback_ptr = pxa_register_debug_serial_streaming();
+		}
+
+		if (!callback_ptr) {
+			pr_err("callback function pointer is %p\n", callback_ptr);
+			return 0;
+		}
+		send_buf_func = callback_ptr->send_buff_cb;
+		if (!send_buf_func)
+			return 0;
+	}
+
+	/* only enable tasklet when it's not enabled */
+	if (atomic_read(&sulog_str->sulog_copy_tasklet.count))
+		tasklet_enable(&sulog_str->sulog_copy_tasklet);
+
+	/*Init statistic values*/
+	sulog_str->sulog_statistic.irq_count = 0;
+	sulog_str->sulog_statistic.packets_dropped = 0;
+	sulog_str->sulog_statistic.packets_pass = 0;
+	sulog_str->sulog_statistic.tot_data_sum = 0;
+	/*Enable IRQ */
+	if (sulog_thru_diag)
+		sulog_str->sulog_driver_status = 2;
+	else
+		sulog_str->sulog_driver_status = 1;
+	enable_irq(sulog_str->res[0].sulog_irq_resource);
+	return 0;
+}
+
+static int sulog_driver_disable(void)
+{
+	int i;
+	struct irq_desc *desc;
+	/* orignal: Disable IRQ */
+	/* new: don't disable irq as ripc0 wakeup irq share
+	* the same irq line with sulog ripc notification irq
+	*/
+	sulog_str->sulog_driver_status = 0; 
+	desc = irq_to_desc(sulog_str->res[0].sulog_irq_resource);
+	if (!(desc && desc->action && desc->action->next)) {
+		DEV_INFO(sulog_str->dev, "disable sulog irq\n");
+		disable_irq(sulog_str->res[0].sulog_irq_resource);
+	}
+
+	/* disable tasklet when it's enabled */
+	if (atomic_read(&sulog_str->sulog_copy_tasklet.count) == 0)
+		tasklet_disable(&sulog_str->sulog_copy_tasklet);
+
+	if (sulog_str->app_mem.sulog_target_type == TARGET_USB) {
+		if (sulog_thru_diag) {
+			pr_info("Disable Sulog through diag port!\n");
+			pxa_unregister_diag_serial_streaming();
+		} else {
+			pxa_unregister_debug_serial_streaming();
+		}
+	}
+	/*Set trace counter back to inital value */
+	DEV_DBG(sulog_str->dev, "disable data copy tasklet\n");
+
+	if (sulog_str->app_mem.sulog_target_type == TARGET_SD) {
+		/* free 2 buffers */
+		for (i = 0; i < NUM_OF_BUFFERS; i++)
+			vfree(sulog_str->app_mem.sulog_app_buf[i]);
+	}
+
+	/* clr pending ripc event if any */
+	if (__raw_readl((unsigned int *)
+		((unsigned int)sulog_str->res[0].ripc_base_address +
+		RIPC_APPS_INT_REG)) & 1) {
+		printk_ratelimited("%s: clr sulog APPS INT\n", __func__);
+		/*Clear LSB to mark interrupt as handled*/
+		__raw_writel(0, (unsigned int *)
+			((unsigned int)sulog_str->res[0].ripc_base_address +
+			RIPC_APPS_INT_REG));
+	}
+
+	DEV_INFO(sulog_str->dev, "Sulog driver disabled\n");
+	return 0;
+}
+
+static int sulog_change_status(unsigned int input)
+{
+	int ret_val = 0;
+
+	DEV_INFO(sulog_str->dev, "Changing sulog driver status. Selected status 0x%x\n", input);
+	switch (input) {
+	case (SULOG_ENABLE2):
+		sulog_thru_diag = 1;
+		/* fall through */
+	case (SULOG_ENABLE):
+		ret_val = sulog_driver_enable();
+		break;
+	case (SULOG_DISABLE):
+		ret_val = sulog_driver_disable();
+		sulog_thru_diag = 0;
+		break;
+	default:
+		/*User request is not valid */
+		DEV_INFO(sulog_str->dev,
+			"Input value is not valid: %d\n", input);
+		ret_val = -ESRCH;
+	}
+	return ret_val;
+}
+
+static ssize_t sulog_driver_show(struct device *dev,
+				 struct device_attribute *attr,
+				 char *buf)
+{
+	return sprintf(buf, "Sulog driver status is 0x%x\n",
+		       sulog_str->sulog_driver_status);
+}
+
+static ssize_t sulog_driver_store(struct device *dev,
+				  struct device_attribute *attr,
+				  const char *buff, size_t count)
+{
+	unsigned int input, ret_val;
+
+	if (sscanf(buff, "%x", &input) < 1) {
+		PR_ERR("Storing information failed\n");
+		return count;
+	}
+
+	if (input != SULOG_ENABLE2)
+		input = (input == 0) ? 0 : 1;
+
+	if (sulog_str->sulog_driver_status == input)
+		return count;
+	ret_val = sulog_change_status(input);
+	if (ret_val)
+		PR_ERR("Sulog driver failed with return code 0x%x\n",
+			ret_val);
+	else
+		DEV_DBG(sulog_str->dev, "Sulog driver status is 0x%x\n",
+			ret_val);
+	return count;
+}
+
+static ssize_t sulog_watermark_show(struct device *dev,
+					struct device_attribute *attr,
+					char *buf)
+{
+	return sprintf(buf,
+			"Sulog Hightwater mark buffer size is 0x%x\n",
+			sulog_str->app_mem.high_watermark);
+}
+
+static ssize_t sulog_watermark_store(struct device *dev,
+				     struct device_attribute *attr,
+				     const char *buff, size_t count)
+{
+	unsigned int input;
+
+	if (sscanf(buff, "0x%x", &input) < 1) {
+		PR_ERR("Reading watermark size from user failed\n");
+		return count;
+	}
+
+	if (sulog_str->app_mem.sulog_allocbuf_size < input)
+		PR_ERR("Requested Watermark level is higher then memory allocation, update it too\n");
+	sulog_str->app_mem.high_watermark = input;
+	DEV_DBG(sulog_str->dev,
+		"High watermark was set to 0x%x\n", input);
+
+	return count;
+}
+
+static ssize_t sulog_buff_size_show(struct device *dev,
+					struct device_attribute *attr,
+					char *buf)
+{
+	return sprintf(buf, "Sulog App buffer size is 0x%08x\n",
+		       sulog_str->app_mem.sulog_allocbuf_size);
+}
+
+static ssize_t sulog_buff_size_store(struct device *dev,
+				     struct device_attribute *attr,
+				     const char *buff, size_t count)
+{
+	unsigned int input;
+
+	if (sulog_str->sulog_driver_status) {
+		PR_ERR("Cannot change memory allocation while driver is running\n");
+		return count;
+	}
+	if (sscanf(buff, "0x%x", &input) < 1) {
+		PR_ERR("Reading buffer size input from user failed\n");
+		return count;
+	}
+	if (sulog_str->app_mem.high_watermark > input)
+		PR_ERR("Current High watermark is larger then requested memory allocation, update it too\n");
+	sulog_str->app_mem.sulog_allocbuf_size = input;
+	DEV_DBG(sulog_str->dev,
+		"App memory allocation was set to 0x%x\n", input);
+
+	return count;
+}
+
+static ssize_t sulog_target_type_show(struct device *dev,
+				 struct device_attribute *attr,
+				 char *buf)
+{
+	if (sulog_str->app_mem.sulog_target_type == TARGET_SD)
+		return sprintf(buf, "Sulog target type is SD\n");
+	else
+		return sprintf(buf, "Sulog target type is USB\n");
+}
+
+static ssize_t sulog_target_type_store(struct device *dev,
+				  struct device_attribute *attr,
+				  const char *buff, size_t count)
+{
+	unsigned int input;
+
+	if (sscanf(buff, "%x", &input) < 1) {
+		PR_ERR("Storing information failed\n");
+		return count;
+	}
+
+	if (sulog_str->sulog_driver_status) {
+		PR_ERR("Cannot change target type while driver is running\n");
+		return count;
+	}
+
+	if (input != TARGET_SD && input != TARGET_USB) {
+		PR_ERR("Wrong input value.\n\
+			0 - Output to SDcard\n\
+			1 - Output to usb\n");
+		return count;
+	}
+
+	sulog_str->app_mem.sulog_target_type = input;
+	if (sulog_str->app_mem.sulog_target_type == TARGET_SD)
+		DEV_INFO(sulog_str->dev, "Sulog target type is SD\n");
+	else
+		DEV_INFO(sulog_str->dev, "Sulog target type is USB\n");
+	return count;
+}
+
+static ssize_t sulog_statistics_show(struct device *dev,
+				 struct device_attribute *attr,
+				 char *buf)
+{
+	return sprintf(buf, "Sulog driver statistics:\n\
+			Number of interrupts received - %d\n\
+			Amount of data passed in bytes 0x%x\n\
+			Number of packates passed - %d\n\
+			Number of packates dropped - %d\n", \
+				sulog_str->sulog_statistic.irq_count, \
+				sulog_str->sulog_statistic.tot_data_sum, \
+				sulog_str->sulog_statistic.packets_pass, \
+				sulog_str->sulog_statistic.packets_dropped);
+}
+
+static ssize_t sulog_statistics_store(struct device *dev,
+				  struct device_attribute *attr,
+				  const char *buff, size_t count)
+{
+	return 0;
+}
+static int sulog_open(struct inode *inode, struct file *file)
+{
+	DEV_DBG(sulog_str->dev, "sulog open command triggered\n");
+	return 0;
+}
+
+static ssize_t sulog_read(struct file *filp, char __user *buf,
+			  size_t count, loff_t *unused)
+{
+	/*Todo - check there is enough space for write*/
+	int ret_val;
+	size_t data_count;
+	ret_val = wait_event_interruptible(sulog_str->read_queue,
+				(sulog_str->app_mem.app_buff_size >
+				 sulog_str->app_mem.high_watermark));
+	if (ret_val == -ERESTARTSYS)
+		return 0;
+
+	if (likely(count >= sulog_str->app_mem.app_buff_size)) {
+		data_count = sulog_str->app_mem.app_buff_size;
+	} else {
+		data_count = count;
+		DEV_INFO(sulog_str->dev, "User space didn't allocate enough read space, possible data lost\n");
+	}
+	/*Moving to other apps buffer*/
+	if (glb_cnt == 0) {
+		sulog_str->app_mem.sulog_app_ptr =
+			sulog_str->app_mem.sulog_app_buf[glb_cnt+1];
+	} else {
+		sulog_str->app_mem.sulog_app_ptr =
+			sulog_str->app_mem.sulog_app_buf[glb_cnt-1];
+	}
+
+	/*AP memory is cyclic*/
+	sulog_str->app_mem.app_buff_size = 0;
+
+	enable_irq(sulog_str->res[0].sulog_irq_resource);
+
+	ret_val = copy_to_user(buf,
+		sulog_str->app_mem.sulog_app_buf[glb_cnt], data_count);
+
+	/*Moving to other apps buffer*/
+	glb_cnt = ((glb_cnt+1) % 2);
+
+	if (unlikely(ret_val)) {
+		DEV_ERR(sulog_str->dev, "copy to user failed\n\n");
+		sulog_str->sulog_statistic.packets_dropped++;
+		return -EFAULT;
+	} else {
+		sulog_str->sulog_statistic.packets_pass++;
+	}
+
+	return data_count;
+}
+
+static irqreturn_t sulog_irq_handler(int this_irq, void *dev_id)
+{
+	unsigned char *tmp_start_addr;
+	/*In order to prevent race conditions */
+	if (unlikely(!sulog_str->sulog_driver_status)) {
+		if (__raw_readl((unsigned int *)
+		    ((unsigned int)sulog_str->res[0].ripc_base_address +
+		    RIPC_APPS_INT_REG)) & 1) {
+			printk_ratelimited(KERN_DEBUG "%s: clr sulog APPS INT\n", __func__);
+			/*Clear LSB to mark interrupt as handled*/
+			__raw_writel(0, (unsigned int *)
+			    ((unsigned int)sulog_str->res[0].ripc_base_address +
+			    RIPC_APPS_INT_REG));
+		}
+		return IRQ_HANDLED;
+	}
+	if (unlikely(!(__raw_readl((unsigned int *)
+			(((unsigned int)sulog_str->
+			    res[0].ripc_base_address +
+			    RIPC_APPS_INFO_REG)))))) {
+		/*Clear LSB to mark interrupt as handled*/
+		__raw_writel(0, (unsigned int *)
+		    ((unsigned int)sulog_str->res[0].ripc_base_address +
+		    RIPC_APPS_INT_REG));
+		return IRQ_HANDLED;
+	}
+
+	sulog_str->res[0].ripc_size =
+		__raw_readl((unsigned int *)
+		    ((unsigned int)sulog_str->res[0].ripc_base_address +
+		    RIPC_APPS_INT_REG));
+	sulog_str->res[0].ripc_size = (sulog_str->res[0].ripc_size >> 1) * 4;
+	/* Check if given size is reasonable */
+	BUG_ON(sulog_str->res[0].ripc_size > COMM_DSP_DOUBLE_BUFFER_SIZE);
+
+	tmp_start_addr = (unsigned char *)__raw_readl((unsigned int *)
+		  (((unsigned int)sulog_str->
+		    res[0].ripc_base_address +
+		    RIPC_APPS_INFO_REG)));
+
+	/* Check if first run or if start address is less than base*/
+	if (unlikely((!sulog_str->res[0].comm_strt_addr)) ||
+		(tmp_start_addr < sulog_str->res[0].comm_base_addr)) {
+		/* First run - go to workqueue to ioremap the whole comm buffer, then continue
+		   or if bug, base address id less than last round, got to remap again*/
+		sulog_str->res[0].comm_base_addr = tmp_start_addr;
+		queue_work(sulog_str->sulog_workqueue_ptr,
+			&sulog_str->sulog_workstruct);
+	} else {
+		/* Not first run - get offset from buffer and continue */
+		sulog_str->res[0].comm_addr_offst =
+				tmp_start_addr - sulog_str->res[0].comm_base_addr;
+		if (likely((sulog_str->res[0].comm_addr_offst >= 0) && (sulog_str->res[0].ripc_size > 0))) {
+			tasklet_schedule(&sulog_str->sulog_copy_tasklet);
+			sulog_str->sulog_statistic.tot_data_sum +=
+				sulog_str->res[0].ripc_size;
+		} else
+		/*We shouldn't reach here, only happen if Current address
+		 is smaller then base address*/
+			sulog_str->sulog_statistic.packets_dropped++;
+	}
+
+	/*Clear LSB to mark interrupt as handled*/
+	__raw_writel(0, (unsigned int *)
+	    ((unsigned int)sulog_str->res[0].ripc_base_address +
+	    RIPC_APPS_INT_REG));
+
+	sulog_str->sulog_statistic.irq_count++;
+
+	return IRQ_HANDLED;
+}
+
+static int sulog_probe(struct platform_device *pdev)
+{
+	int ret_val, irq, fs_cnt, i = 0;
+	unsigned long flags;
+
+	sulog_str = kzalloc(sizeof(struct sulog_struct), GFP_KERNEL);
+	if (sulog_str == NULL) {
+		PR_ERR("Failed to allocate memory!\n");
+		ret_val = -ENOMEM;
+		goto err_mem;
+	}
+	ret_val = misc_register(&sulog_miscdev);
+	if (ret_val != 0) {
+		PR_ERR("misc_register failed\n");
+		goto err_misc_register;
+	}
+	sulog_str->dev = sulog_miscdev.this_device;
+	/* Submit resourses */
+	for (i = 0; i < RIPC_BASE_ADDRESSES; i++) {
+		sulog_str->res[i].resource = platform_get_resource(
+			pdev, IORESOURCE_MEM, i);
+		if (sulog_str->res[i].resource == NULL) {
+			PR_ERR("Failed to receive driver resources\n");
+			ret_val = -ENOMEM;
+			goto err_malloc;
+		}
+	#if 0
+		if (!request_mem_region(
+				sulog_str->res[i].resource->start,
+				resource_size(sulog_str->res[i].resource),
+				sulog_str->res[i].resource->name)) {
+			ret_val = -ENOMEM;
+			goto mem_reg;
+		}
+	#endif
+		sulog_str->res[i].ripc_base_address = ioremap(
+			sulog_str->res[i].resource->start,
+			resource_size(sulog_str->res[i].resource));
+
+		if (sulog_str->res[i].ripc_base_address == NULL) {
+			DEV_ERR(sulog_str->dev,
+				"failed to get platform register values!\n");
+			ret_val = -ENXIO;
+			goto err_malloc;
+		}
+	}
+
+	/*Set default values */
+	sulog_str->app_mem.sulog_allocbuf_size = AP_DDR_DEFAULT_SIZE;
+	sulog_str->app_mem.high_watermark = AP_HIGHWATERMARK;
+	sulog_str->app_mem.app_buff_size = 0;
+	sulog_str->sulog_driver_status = 0;
+	sulog_str->app_mem.sulog_target_type = TARGET_USB;
+	platform_set_drvdata(pdev, sulog_str);
+	/*Submit IRQ */
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
+		DEV_ERR(sulog_str->dev, "failed to get irq!\n");
+		ret_val = -ENODEV;
+		goto err_malloc;
+	}
+	local_irq_save(flags);
+	ret_val = request_irq(irq, sulog_irq_handler,
+			      /* IRQF_DISABLED | */IRQF_SHARED, pdev->name, sulog_str);
+	if (ret_val) {
+		DEV_ERR(sulog_str->dev, "failed to request irq!\n");
+		local_irq_restore(flags);
+		goto err_malloc;
+	}
+	disable_irq(irq);
+	local_irq_restore(flags);
+	/*Todo-add irq name*/
+	sulog_str->res[0].sulog_irq_resource = irq;
+	/*Todo - register on DVFM */
+	/* Create sysfs files */
+	for (fs_cnt = 0; fs_cnt < ARRAY_SIZE(dev_attr_arr); fs_cnt++) {
+		ret_val = device_create_file(sulog_str->dev,
+					     dev_attr_arr[fs_cnt]);
+		if (ret_val < 0) {
+			DEV_ERR(sulog_str->dev, "failed to create sysfs!\n");
+			goto err_sysfs;
+		}
+	}
+	init_waitqueue_head(&sulog_str->read_queue);
+	/*Init workqueue*/
+	sulog_str->sulog_workqueue_ptr = create_workqueue("sulog_workqueue");
+	if (!sulog_str->sulog_workqueue_ptr) {
+		DEV_ERR(sulog_str->dev, "create workqueue error\n");
+		ret_val = -ENOMEM;
+		goto err_sysfs;
+	}
+	INIT_WORK(&sulog_str->sulog_workstruct, sulog_comm_map);
+	tasklet_init(&sulog_str->sulog_copy_tasklet,
+			sulog_copy_tasklet, 0);
+	DEV_INFO(sulog_str->dev, "sulog_driver_init finished successfully\n");
+	return 0;
+err_sysfs:
+	for (fs_cnt--; fs_cnt >= 0; fs_cnt--)
+		device_remove_file(sulog_str->dev,
+					dev_attr_arr[fs_cnt]);
+mem_reg:
+	for (; i >= 0 ; i--)
+		release_mem_region(sulog_str->res[i].resource->start,
+			resource_size(sulog_str->res[i].resource));
+err_malloc:
+	kfree(sulog_str);
+	if (i-- > 0)
+		goto mem_reg;
+err_mem:
+	misc_deregister(&sulog_miscdev);
+err_misc_register:
+	PR_ERR("dbg_macro probe error %d\n", ret_val);
+	return ret_val;
+}
+
+static int sulog_remove(struct platform_device *pdev)
+{
+	int i, fs_cnt = ARRAY_SIZE(dev_attr_arr);
+
+	disable_irq(sulog_str->res[0].sulog_irq_resource);
+	destroy_workqueue(sulog_str->sulog_workqueue_ptr);
+	if (sulog_str->res[0].comm_strt_addr && sulog_str->res[0].remapped)
+		iounmap(sulog_str->res[0].comm_strt_addr);
+	/* free 2 buffers */
+	for (i = 0; i < NUM_OF_BUFFERS; i++)
+		vfree(sulog_str->app_mem.sulog_app_buf[i]);
+	for (fs_cnt--; fs_cnt >= 0; fs_cnt--)
+		device_remove_file(sulog_str->dev,
+					dev_attr_arr[fs_cnt]);
+	for (i = 0; i < RIPC_BASE_ADDRESSES; i++) {
+		release_mem_region(sulog_str->res[i].resource->start,
+			resource_size(sulog_str->res[i].resource));
+	}
+	kfree(sulog_str);
+	misc_deregister(&sulog_miscdev);
+	sulog_miscdev.minor = MISC_DYNAMIC_MINOR;
+	DEV_INFO(sulog_str->dev,
+		 "Sulog remove completed\n");
+	return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id sulog_of_match[] = {
+	{.compatible = "mrvl,mmp-sulog",},
+	{},
+};
+
+MODULE_DEVICE_TABLE(of, sulog_of_match);
+#endif
+static struct platform_driver sulog_driver = {
+	.probe = sulog_probe,
+	.remove = sulog_remove,
+	.driver = {
+			.name = "mmp-sulog",
+			.owner = THIS_MODULE,
+#ifdef CONFIG_OF
+			.of_match_table = sulog_of_match,
+#endif
+		},
+};
+
+static int __init sulog_init(void)
+{
+	return platform_driver_register(&sulog_driver);
+}
+
+static void __exit sulog_exit(void)
+{
+	platform_driver_unregister(&sulog_driver);
+}
+
+module_init(sulog_init);
+module_exit(sulog_exit);
+
+MODULE_AUTHOR("Tzviel Lemberger <tzviel@marvell.com>");
+MODULE_DESCRIPTION("Super log driver");
+MODULE_LICENSE("GPL");