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");