#include <linux/module.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <asm/irq.h>
#include "mailbox.h"
MODULE_LICENSE("Dual BSD/GPL");
int mailbox_major = MAILBOX_MAJOR;
int mailbox_minor =   0;
struct cdev cdev; 
struct mailbox_dev* mailbox_devp;
static DECLARE_WAIT_QUEUE_HEAD(wq);
static unsigned int message,message_m3;
static int flag = 0;

void DSPSSClkEnable(int DSPSSBOOTADDR,int DSPSSBOOTADDRVALUE,int RM_DSP_RSTCTRL, int CM_DSP_CLKSTCTRL, int CM_DSP_DSP_CLKCTRL,
				int RM_DSP_RSTST, int DSP_L2_SRAM_TARG)
{
    int i, fail = 0;
    printk("--->>> DSP Initialization is in progress ... <<<---\n");

    /* DSPSS Boot Address */
    WR_MEM_32(DSPSSBOOTADDR, (DSPSSBOOTADDRVALUE >> 10));

    /* Ware reset asserted for DSP_LRST, DSP Cache and Slave */
    WR_MEM_32(RM_DSP_RSTCTRL, 0x3);

    /* Start a SW force wakeup for DSPSS */
    WR_MEM_32(CM_DSP_CLKSTCTRL, 0x2);
    /* Enable DSPSS clock */
    WR_MEM_32(CM_DSP_DSP_CLKCTRL, 0x1);

    /* Check whether GFCLK is gated or not */
    while ((RD_MEM_32(CM_DSP_CLKSTCTRL) & 0x100) != 0x100);
    printk("DEBUG: Clock is active ... \n");


    /* Reset de-assertion for DSPSS */
    WR_MEM_32(RM_DSP_RSTCTRL, 0x1); 
    /* Check the reset state: DSPSS */
    while ((RD_MEM_32(RM_DSP_RSTST) & 0x2) != 0x2);

    WR_MEM_32(DSP_L2_SRAM_TARG, 0x0000A120); //Self branch loop for DSP

     /* Check module mode */
    while ((RD_MEM_32(CM_DSP_DSP_CLKCTRL) & 0x30000) != 0x0);

    printk("DEBUG: Checking for data integrity in DSPSS L2RAM ... \n");
    for (i = 1; i < 16; i++)
    {
        WR_MEM_32((DSP_L2_SRAM_TARG + (i << 2)), 0x12345678);
    }

    for (i = 1; i < 16; i++)
    {
        if (RD_MEM_32(DSP_L2_SRAM_TARG + (i << 2)) != 0x12345678)
        {
            fail++;
            break;
        }
    }

    if (fail)
    {
        printk("ERROR: Data integrity check in GEM L2RAM has failed! \n");
    }
    else
    {
        printk("DEBUG: Data integrity check in GEM L2RAM is sucessful! \n");
    }


    printk("--->>> DSPInitialization is DONE! <<<---\n");
}
void DSP_start(int DSPSSBOOTADDR,int DSPSSBOOTADDRVALUE,int RM_DSP_RSTCTRL, int RM_DSP_RSTST)
{
	WR_MEM_32(DSPSSBOOTADDR,(DSPSSBOOTADDRVALUE>>10) ); /*DSPBOOT ADDRESS*/
	WR_MEM_32(RM_DSP_RSTCTRL, 	0); /*Bring GEM Core out of Reset*/
	
    while(((RD_MEM_32(RM_DSP_RSTST)&0x3))!=0x3); /*GEM core core is out of Reset*/

}


void dsp_start(int entry,int dsp)
{
	if(dsp == 1)
	{
		DSP_start( DSPSS1BOOTADDR, entry, RM_DSP1_RSTCTRL,  RM_DSP1_RSTST);
	}else if(dsp == 2)
	{
		DSP_start( DSPSS2BOOTADDR, entry, RM_DSP2_RSTCTRL,  RM_DSP2_RSTST);
	}
}

//mpu use mailbox5 user2
void sendmsg_mpu(int msg)
{
	WR_MEM_32(MAILBOX_IRQENABLE_SET_U_MPU,RD_MEM_32(MAILBOX_IRQENABLE_SET_U_MPU) | (0x1<<4));
	WR_MEM_32(MAILBOX_MESSAGE_MPU,msg);
}
//dsp2 mailbox13 user2
void sendmsg_dsp2(int msg)
{
	WR_MEM_32(MAILBOX_IRQENABLE_SET_U_DSP2,RD_MEM_32(MAILBOX_IRQENABLE_SET_U_DSP2) | (0x1<<4));
	WR_MEM_32(MAILBOX_MESSAGE_DSP2,msg);
}
//dsp2 mailbox13 user1
void sendmsg_dsp1(int msg)
{
	WR_MEM_32(MAILBOX_IRQENABLE_SET_U_DSP1,RD_MEM_32(MAILBOX_IRQENABLE_SET_U_DSP1) | (0x1<<2));
	WR_MEM_32(MAILBOX_MESSAGE_DSP1,msg);
}
int mailbox_open(struct inode *inode, struct file *filp)
{
    struct mailbox_dev *dev;
     
    int num = MINOR(inode->i_rdev);
 
    if (num >= 1) 
            return -ENODEV;
    dev = &mailbox_devp[num];
     

    filp->private_data = dev; 
    // printk("mailbox_open\n");
    return 0; 
}
 

int mailbox_release(struct inode *inode, struct file *filp)
{
  //   printk("mailbox_release\n");
  return 0;
}
 

static ssize_t mailbox_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos)
{
  unsigned long p =  *ppos;
  unsigned int count = size;
  int ret = 0;
  struct mailbox_dev *dev = filp->private_data; 
 
  
 printk("mailbox_read\n");
  return ret;
}
 

static ssize_t mailbox_write(struct file *filp, const char __user *buf, size_t size, loff_t *ppos)
{

  int ret = 0;
  struct mailbox_devp *dev = filp->private_data; 
   
    printk("mailbox_write\n");
  return ret;
}
 

static loff_t mailbox_llseek(struct file *filp, loff_t offset, int whence)
{ 
    
    return 0;
 
}
static long mailbox_ioctl(struct file *filp,
                 unsigned int cmd, unsigned long arg)
{

	int err = 0, tmp,entry;
	int retval = 0;
    
	/*
	 * extract the type and number bitfields, and don't decode
	 * wrong cmds: return ENOTTY (inappropriate ioctl) before access_ok()
	 */
	if (_IOC_TYPE(cmd) != MAILBOX_IOC_MAGIC) return -ENOTTY;
	if (_IOC_NR(cmd) > MAILBOX_IOC_MAXNR) return -ENOTTY;

	/*
	 * the direction is a bitmask, and VERIFY_WRITE catches R/W
	 * transfers. `Type' is user-oriented, while
	 * access_ok is kernel-oriented, so the concept of "read" and
	 * "write" is reversed
	 */
	if (_IOC_DIR(cmd) & _IOC_READ)
		err = !access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd));
	else if (_IOC_DIR(cmd) & _IOC_WRITE)
		err =  !access_ok(VERIFY_READ, (void __user *)arg, _IOC_SIZE(cmd));
	if (err) return -EFAULT;

	switch(cmd) {

	  case MAILBOX_DSP1_START:
		if (! capable (CAP_SYS_ADMIN))
			return -EPERM;
		retval = __get_user(entry, (int __user *)arg);
		dsp_start(entry,1);
		break;
 	  case MAILBOX_SENDMSG_DSP1:
		if (! capable (CAP_SYS_ADMIN))
			return -EPERM;
		retval = __get_user(entry, (int __user *)arg);
		sendmsg_dsp1(entry);
		break;       
 	  case MAILBOX_RECVMSG:
		if (! capable (CAP_SYS_ADMIN))
			return -EPERM;
		wait_event_interruptible(wq,flag!=0);
		flag = 0;
		retval = __put_user(message, (int __user *)arg);
		break;       
	  case MAILBOX_DSP2_START:
		if (! capable (CAP_SYS_ADMIN))
			return -EPERM;
		retval = __get_user(entry, (int __user *)arg);
		printk("MAILBOX_M3_START entry 0x%x\n ",entry);
		dsp_start(entry,2);
		break;
 	  case MAILBOX_SENDMSG_DSP2:
		if (! capable (CAP_SYS_ADMIN))
			return -EPERM;
		retval = __get_user(entry, (int __user *)arg);
		sendmsg_dsp2(entry);
		break;       
     

	  default:  /* redundant, as cmd was checked against MAXNR */
		return -ENOTTY;
	}
	return retval;

}
static const struct file_operations mailbox_fops =
{
	.owner = THIS_MODULE,
	.llseek = mailbox_llseek,
	.read = mailbox_read,
	.write = mailbox_write,
	.unlocked_ioctl  = mailbox_ioctl,
	.open = mailbox_open,
	.release = mailbox_release,

};
/*this is the mailbox13 irq*/
static irqreturn_t mbox_interrupt(int irq, void *p)
{
	int i;
//	printk("mbox_interrupt\n");
	if((RD_MEM_32(MAILBOX_IRQSTATUS_CLR_U_MPU) != 0))
	//printk("MAILBOX_IRQSTATUS_CLR_U0 0x%x\n",RD_MEM_32(MAILBOX_IRQSTATUS_CLR_U0));
	if(RD_MEM_32(MAILBOX_IRQSTATUS_CLR_U_MPU)&(0x1<<4))
    {
	//	printk("MAILBOX_MSGSTATUS1 0x%x\n",RD_MEM_32(MAILBOX_MSGSTATUS1));
		while(RD_MEM_32(MAILBOX_MSGSTATUS_MPU))
    	{
    		message = RD_MEM_32(MAILBOX_MESSAGE_MPU);
	//		printk("IRQ message=0x%x\n",message);
    	}
    	WR_MEM_32(MAILBOX_IRQSTATUS_CLR_U_MPU,(0x1<<4));
		flag = 1;
		wake_up_interruptible(&wq);
	}
	
	return IRQ_HANDLED;
}
static int mailbox_init(void)
{
	int result,i;
	dev_t devno = MKDEV(mailbox_major, mailbox_minor);
	if (mailbox_major) {
		result = register_chrdev_region(devno, 1, "dsp_mailbox");
	} else {
		result = alloc_chrdev_region(&devno, mailbox_major, 1,
				"dsp_mailbox");
		mailbox_major = MAJOR(devno);
	}
	if (result < 0) {
		printk(KERN_WARNING "dsp_mailbox: can't get major %d\n", mailbox_major);
		return result;
	}
	cdev_init(&cdev, &mailbox_fops);
	cdev.owner = THIS_MODULE;
	cdev.ops = &mailbox_fops;
   
	cdev_add(&cdev, devno, 1);
	mailbox_devp = kmalloc(sizeof(struct mailbox_dev),GFP_KERNEL);
	if (!mailbox_devp)    
	{
		result =  - ENOMEM;
		goto fail_malloc;
	}
	memset(mailbox_devp, 0, sizeof(struct mailbox_dev));
	
 

	v_MAILBOX5_BASE_ADDR = ioremap_nocache(MAILBOX5_BASE_ADDR,0x1000);
	v_MAILBOX13_BASE_ADDR = ioremap_nocache(MAILBOX13_BASE_ADDR,0x1000);
	//printk("v_MAILBOX_BASE_ADDR 0x%x\n",v_MAILBOX_BASE_ADDR);	
	MAILBOX_MESSAGE_DSP1 = v_MAILBOX13_BASE_ADDR+0x40+0x4*1;
	MAILBOX_FIFOSTATUS_DSP1  = v_MAILBOX13_BASE_ADDR+0x80+0x4*1;
	MAILBOX_MSGSTATUS_DSP1 = v_MAILBOX13_BASE_ADDR+0xc0+0x4*1;
	MAILBOX_IRQSTATUS_RAW_U_DSP1 = v_MAILBOX13_BASE_ADDR+0x100+0x10*1;
	MAILBOX_IRQSTATUS_CLR_U_DSP1 = v_MAILBOX13_BASE_ADDR+0x104+0x10*1;
	MAILBOX_IRQENABLE_SET_U_DSP1 = v_MAILBOX13_BASE_ADDR+0x108+0x10*1;
	MAILBOX_IRQENABLE_CLR_U_DSP1 = v_MAILBOX13_BASE_ADDR+0x10c+0x10*1;

	MAILBOX_MESSAGE_DSP2 = v_MAILBOX13_BASE_ADDR+0x40+0x4*2;
	MAILBOX_FIFOSTATUS_DSP2  = v_MAILBOX13_BASE_ADDR+0x80+0x4*2;
	MAILBOX_MSGSTATUS_DSP2 = v_MAILBOX13_BASE_ADDR+0xc0+0x4*2;
	MAILBOX_IRQSTATUS_RAW_U_DSP2 = v_MAILBOX13_BASE_ADDR+0x100+0x10*2;
	MAILBOX_IRQSTATUS_CLR_U_DSP2 = v_MAILBOX13_BASE_ADDR+0x104+0x10*2;
	MAILBOX_IRQENABLE_SET_U_DSP2 = v_MAILBOX13_BASE_ADDR+0x108+0x10*2;
	MAILBOX_IRQENABLE_CLR_U_DSP2 = v_MAILBOX13_BASE_ADDR+0x10c+0x10*2;
	
	MAILBOX_MESSAGE_MPU = v_MAILBOX5_BASE_ADDR+0x40+0x4*2;
	MAILBOX_FIFOSTATUS_MPU  = v_MAILBOX5_BASE_ADDR+0x80+0x4*2;
	MAILBOX_MSGSTATUS_MPU = v_MAILBOX5_BASE_ADDR+0xc0+0x4*2;
	MAILBOX_IRQSTATUS_RAW_U_MPU = v_MAILBOX5_BASE_ADDR+0x100+0x10*2;
	MAILBOX_IRQSTATUS_CLR_U_MPU = v_MAILBOX5_BASE_ADDR+0x104+0x10*2;
	MAILBOX_IRQENABLE_SET_U_MPU = v_MAILBOX5_BASE_ADDR+0x108+0x10*2;
	MAILBOX_IRQENABLE_CLR_U_MPU = v_MAILBOX5_BASE_ADDR+0x10c+0x10*2;
	
	v_DSP1_L2_SRAM_TARG = ioremap_nocache(DSP1_L2_SRAM_TARG,0x1000);
	v_DSP2_L2_SRAM_TARG = ioremap_nocache(DSP2_L2_SRAM_TARG,0x1000);
	v_CTRL_MODULE_CORE = ioremap_nocache(CTRL_MODULE_CORE ,0x1000);
	v_DSP1_CM_CORE_AON = ioremap_nocache(DSP1_CM_CORE_AON ,0x1000);
	v_DSP2_CM_CORE_AON = ioremap_nocache(DSP2_CM_CORE_AON ,0x1000);
	v_DSP1_PRM = ioremap_nocache(DSP1_PRM ,0x1000);
	v_DSP2_PRM = ioremap_nocache(DSP2_PRM ,0x1000);

/*******************************************************************************
        DSPSS1 Address Map defines: CortexA15_CPU1 view
*******************************************************************************/
    CM_DSP1_CLKSTCTRL      =      (v_DSP1_CM_CORE_AON + 0x0);
	CM_DSP1_DSP1_CLKCTRL    =     (v_DSP1_CM_CORE_AON + 0x20);
	RM_DSP1_RSTCTRL       =       (v_DSP1_PRM + 0x10);
	RM_DSP1_RSTST        =        (v_DSP1_PRM + 0x14);
	DSPSS1BOOTADDR       =        (v_CTRL_MODULE_CORE + 0x55C);
	DSPSS1BOOTADDRVALUE   =       (DSP1_L2_SRAM_TARG - 0x40000000);

/*******************************************************************************
        DSPSS2 Address Map defines: CortexA15_CPU1 view
*******************************************************************************/
	CM_DSP2_CLKSTCTRL     =       (v_DSP2_CM_CORE_AON + 0x0);
	CM_DSP2_DSP2_CLKCTRL   =      (v_DSP2_CM_CORE_AON + 0x20);
	RM_DSP2_RSTCTRL        =      (v_DSP2_PRM + 0x10);
	RM_DSP2_RSTST          =      (v_DSP2_PRM + 0x14);
	DSPSS2BOOTADDR         =      (v_CTRL_MODULE_CORE + 0x560);
	DSPSS2BOOTADDRVALUE     =     (DSP2_L2_SRAM_TARG - 0x40800000);
		

	
	printk(KERN_ALERT "insmod dsp mailbox driver\n");
	DSPSSClkEnable(DSPSS1BOOTADDR,0x800000,RM_DSP1_RSTCTRL,  CM_DSP1_CLKSTCTRL,  CM_DSP1_DSP1_CLKCTRL, RM_DSP1_RSTST,  v_DSP1_L2_SRAM_TARG);
	DSPSSClkEnable(DSPSS2BOOTADDR,0x800000,RM_DSP2_RSTCTRL,  CM_DSP2_CLKSTCTRL,  CM_DSP2_DSP2_CLKCTRL, RM_DSP2_RSTST,  v_DSP2_L2_SRAM_TARG);
	result = request_irq(313, mbox_interrupt, IRQF_SHARED,"dsp_mailbox", mailbox_devp);	// MPU mailbox interrupt
	if (unlikely(result)) {
			printk("failed to register mailbox interrupt:%d\n",result);
			goto fail_request_irq;
	}
	return 0;

fail_malloc:
	unregister_chrdev_region(devno, 1);
fail_request_irq:
	free_irq(308, mailbox_devp);
	return result;	
}

static void mailbox_exit(void)
{
	int i;
	free_irq(308, mailbox_devp);
	iounmap(v_MAILBOX5_BASE_ADDR);
	iounmap(v_MAILBOX13_BASE_ADDR);
	iounmap(v_DSP1_L2_SRAM_TARG);
	iounmap(v_DSP2_L2_SRAM_TARG);
	iounmap(v_CTRL_MODULE_CORE);
	iounmap(v_DSP1_CM_CORE_AON);
	iounmap(v_DSP2_CM_CORE_AON);
	iounmap(v_DSP1_PRM);
	iounmap(v_DSP2_PRM);
	dev_t devno = MKDEV(mailbox_major, mailbox_minor);
	cdev_del(&cdev);   
	kfree(mailbox_devp); 
	/* cleanup_module is never called if registering failed */
	unregister_chrdev_region(devno, 1);

	/* and call the cleanup functions for friend devices */
	printk(KERN_ALERT "rmmod dsp mailbox driver\n");
}

module_init(mailbox_init);
module_exit(mailbox_exit);
