#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/system.h>
#include <asm/uaccess.h>
#include <linux/slab.h>
#include <plat/mailbox.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;
static int notify_mailbox_dsp_isr(struct notifier_block *nb, unsigned long val,
								void *ntfy_msg)
{
	/* Decode the msg to identify the processor that has sent the message */
	//printk("Mailbox notify msg 0x%x\n",*((unsigned int*)ntfy_msg));
	return 0;
}
static struct notifier_block ti81xx_dsp_notify_nb = {
	.notifier_call = notify_mailbox_dsp_isr,
};
void DucatiClkEnable(int entry) 
{
    int i,tmp;
                                 
    //while(RD_MEM_32(CM_DEFAULT_DUCATI_CLKSTCTRL)!=0x1);

    WR_MEM_32(RM_DEFAULT_RSTCTRL,   0x7); /*Reset first M3*/
    WR_MEM_32(CM_DEFAULT_DUCATI_CLKSTCTRL, 2); /*Enable Power Domain Transition*/
    WR_MEM_32(CM_DEFAULT_DUCATI_CLKCTRL,   2); /*Enable Ducati Clocks*/
	//printk("111111\n");
    /*Check CLKIN200TR & CLKINTR  are active*/
    while(((RD_MEM_32(CM_DEFAULT_DUCATI_CLKSTCTRL)&0x300)>>8)!=3) ;
    //	printk("2222\n");                              

    //----------------------WR_MEM_32(RM_DEFAULT_RSTCTRL,   0xEF); /*Enable the Ducati Logic*/
    //--WR_MEM_32(RM_DEFAULT_RSTCTRL,   0xE3); /*Enable the Ducati Logic*/

    //  for(i=0;i<100;i++);
    //    GEL_TextOut("\tDelay gone  \n","Output",1,1,1);
    /*Write Ducate IRAM Boot Image */
  //  WR_MEM_32(DUCATI_BASE_ADDR,       0x10000);printk("aaa\n");
    WR_MEM_32(DUCATI_BASE_ADDR+0x4,   entry);//printk("bbb\n");
  //  WR_MEM_32(DUCATI_BASE_ADDR+0x8,   0xE7FEE7FE);printk("ccc\n");
    //  GEL_TextOut("\tAfter RAM written  \n","Output",1,1,1);                                      
 #if 0		//MMU has been set by syslink
    /*Large Page Translations */
    WR_MEM_32(DUCATI_MMU_CFG+0x800,         0x40000000);
    WR_MEM_32(DUCATI_MMU_CFG+0x804,         0x9A000000);
    WR_MEM_32(DUCATI_MMU_CFG+0x808,         0x9C000000);
    WR_MEM_32(DUCATI_MMU_CFG+0x80C,         0x9E000000);
	//printk("33333\n");
    WR_MEM_32(DUCATI_MMU_CFG+0x820,         0x40000000);
    WR_MEM_32(DUCATI_MMU_CFG+0x824,         0x9A000000);
    WR_MEM_32(DUCATI_MMU_CFG+0x828,         0x9C000000);
    WR_MEM_32(DUCATI_MMU_CFG+0x82C,         0x9E000000);

    WR_MEM_32(DUCATI_MMU_CFG+0x840,         0x00000003);	//page 0 512M
    WR_MEM_32(DUCATI_MMU_CFG+0x844,         0x00030001);	//32M
    WR_MEM_32(DUCATI_MMU_CFG+0x848,         0x00020001);
    WR_MEM_32(DUCATI_MMU_CFG+0x84C,         0x00020001);
	//printk("4444\n");
    /*Medium Page*/
    WR_MEM_32(DUCATI_MMU_CFG+0x860,         0x00300000);
    WR_MEM_32(DUCATI_MMU_CFG+0x864,         0x00400000);
    //WR_MEM_32(DUCATI_MMU_CFG+0x868,       0x00000000);
    //WR_MEM_32(DUCATI_MMU_CFG+0x86C,       0x00000000);

    WR_MEM_32(DUCATI_MMU_CFG+0x8A0,         0x40300000);
    WR_MEM_32(DUCATI_MMU_CFG+0x8A4,         0x40400000);
    //WR_MEM_32(DUCATI_MMU_CFG+0x8A8,       0x00000000);
    //WR_MEM_32(DUCATI_MMU_CFG+0x8AC,       0x00000000);

    WR_MEM_32(DUCATI_MMU_CFG+0x8E0,       0x00000003);
    WR_MEM_32(DUCATI_MMU_CFG+0x8E4,       0x00020003);
    //WR_MEM_32(DUCATI_MMU_CFG+0x8E8,         0x00000000);
    //WR_MEM_32(DUCATI_MMU_CFG+0x8EC,         0xC0000000);

    /*Small Page*/

    WR_MEM_32(DUCATI_MMU_CFG+0x920,       0x00000000);
    WR_MEM_32(DUCATI_MMU_CFG+0x924,       0x40000000);
    WR_MEM_32(DUCATI_MMU_CFG+0x928,       0x00004000);
    WR_MEM_32(DUCATI_MMU_CFG+0x92C,       0x00008000);
    WR_MEM_32(DUCATI_MMU_CFG+0x930,       0x0000C000);

    WR_MEM_32(DUCATI_MMU_CFG+0x9A0,       0x55020000);
    WR_MEM_32(DUCATI_MMU_CFG+0x9A4,       0x55080000);
    WR_MEM_32(DUCATI_MMU_CFG+0x9A8,       0x55024000);
    WR_MEM_32(DUCATI_MMU_CFG+0x9AC,       0x55028000);
    WR_MEM_32(DUCATI_MMU_CFG+0x9B0,       0x5502C000);

    WR_MEM_32(DUCATI_MMU_CFG+0xA20,       0x0001000B);
    WR_MEM_32(DUCATI_MMU_CFG+0xA24,       0x0000000B);
    WR_MEM_32(DUCATI_MMU_CFG+0xA28,       0x00010003);
    WR_MEM_32(DUCATI_MMU_CFG+0xA2C,       0x00000003);
    WR_MEM_32(DUCATI_MMU_CFG+0xA30,       0x00000003);
#endif                                     
	//printk("5555\n");
	tmp = RD_MEM_32(RM_DEFAULT_RSTCTRL);
	//printk("tmp = 0x%x\n",tmp);
	tmp = tmp&(~4);
    WR_MEM_32(RM_DEFAULT_RSTCTRL,   tmp); 

    /*Check for Ducati M3_0 & M3_1 out of Reset*/
    while(((RD_MEM_32(RM_DEFAULT_RSTST)&0x14))!=0x14) ;
    //GEL_TextOut("\tDucati is out of Reset\n","Output",1,1,1);                                       
	//printk("6666\n");
    /*Check Module is in Functional Mode */
    while(((RD_MEM_32(CM_DEFAULT_DUCATI_CLKCTRL)&0x30000)>>16)!=0) ;
     //	printk("777\n");                               
}
void DSPWake()
{
	unsigned int i=0, k=0, fail=0;

	//printk("\tPRCM for GEMSS is in Progress, Please wait.....  \n");	 									

	/*Cortex A8 must be in Supervisor Mode to Access the following two registers*/
//	WR_MEM_32(DSPMEM_SLEEP, 		0); /*Bring GEM L2RAM out of Power down Mode*/
	WR_MEM_32(DSPBOOTADDR, 		    0x800000); /*DSPBOOT ADDRESS*/
	
	//printk("\t CP0...Done");
	
	/*Enable Clock to MMU CFG*/
	WR_MEM_32(CM_MMUCFG_CLKSTCTRL, 		2); 
	WR_MEM_32(CM_ALWON_MMUCFG_CLKCTRL, 		2); 
	while(((RD_MEM_32(CM_ALWON_MMUCFG_CLKCTRL)&0x0))!=0x0); 
	while(((RD_MEM_32(CM_MMUCFG_CLKSTCTRL)&0x100))!=0x100); 

	//printk("\t CP1...Done");

	/*Enable Clock to Data*/
	WR_MEM_32(CM_MMU_CLKSTCTRL, 		2); 
	WR_MEM_32(CM_ALWON_MMUDATA_CLKCTRL, 		2); 
	while(((RD_MEM_32(CM_ALWON_MMUDATA_CLKCTRL)&0x0))!=0x0); 
	while(((RD_MEM_32(CM_MMU_CLKSTCTRL)&0x100))!=0x100); 

	//printk("\t CP2...Done");

    /*Enable Clock to GEMSS*/
	WR_MEM_32(CM_GEM_CLKSTCTRL, 		2); /*Enable Power Domain Transition*/
	while(RD_MEM_32(PM_ACTIVE_PWRSTST)!=0x37);	/*Check Power is ON*/
	WR_MEM_32(CM_ACTIVE_GEM_CLKCTRL, 	2); /*Enable GEMSS Clocks*/

	//printk("\t CP3...Done");

	while(((RD_MEM_32(CM_GEM_CLKSTCTRL)&0x700))!=0x700); /*GME Clocks are Active*/
	WR_MEM_32(RM_ACTIVE_RSTCTRL, 	1); /*Issue GEM Warm Reset To access GEMSS memories */
    while(((RD_MEM_32(RM_ACTIVE_RSTST)&0x2))!=0x2); /*GEMSS Warm Reset has been Asserted*/

}
void GEMSSClkEnable(unsigned int entry)
{
	unsigned int i=0, k=0, fail=0;


	/*Cortex A8 must be in Supervisor Mode to Access the following two registers*/
//	WR_MEM_32(DSPMEM_SLEEP, 		0); /*Bring GEM L2RAM out of Power down Mode*/
	WR_MEM_32(DSPBOOTADDR, 		    entry); /*DSPBOOT ADDRESS*/
	WR_MEM_32(RM_ACTIVE_RSTCTRL, 	0); /*Bring GEM Core out of Reset*/
	
    while(((RD_MEM_32(RM_ACTIVE_RSTST)&0x3))!=0x3); /*GEM core core is out of Reset*/
			
}

void dsp_start(int entry)
{
    GEMSSClkEnable(entry);
}
void m3_start(int entry)
{
    DucatiClkEnable(entry);
}
/*
We use mailbox10 to send the message from A8 to DSP
*/
void arm2dsp(int msg)
{
	//printk("send msg 0x%x\n",msg);
	WR_MEM_32(MAILBOX_IRQENABLE_SET_U1,RD_MEM_32(MAILBOX_IRQENABLE_SET_U1) | (0x1<<20));
	WR_MEM_32(MAILBOX_MESSAGE,msg);
}
/*
We use mailbox9 to send the message from A8 to M3 RTOS core u=2
*/
void arm2m3(int msg)
{
	//printk("send msg 0x%x\n",msg);
	WR_MEM_32(MAILBOX_IRQENABLE_SET_U2,RD_MEM_32(MAILBOX_IRQENABLE_SET_U2) | (0x1<<18));
	WR_MEM_32(MAILBOX_MESSAGE_M3,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 int 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_DSP_START:
		if (! capable (CAP_SYS_ADMIN))
			return -EPERM;
		retval = __get_user(entry, (int __user *)arg);
		dsp_start(entry);
		break;
 	  case MAILBOX_SENDMSG:
		if (! capable (CAP_SYS_ADMIN))
			return -EPERM;
		retval = __get_user(entry, (int __user *)arg);
		arm2dsp(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_M3_START:
		if (! capable (CAP_SYS_ADMIN))
			return -EPERM;
		retval = __get_user(entry, (int __user *)arg);
		printk("MAILBOX_M3_START entry 0x%x\n ",entry);
		m3_start(entry);
		break;
 	  case MAILBOX_SENDMSG_M3:
		if (! capable (CAP_SYS_ADMIN))
			return -EPERM;
		retval = __get_user(entry, (int __user *)arg);
		arm2m3(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 mailbox11 irq*/
static irqreturn_t mbox_interrupt(int irq, void *p)
{
	int i;
	if((RD_MEM_32(MAILBOX_IRQSTATUS_CLR_U0) != 0))
	//printk("MAILBOX_IRQSTATUS_CLR_U0 0x%x\n",RD_MEM_32(MAILBOX_IRQSTATUS_CLR_U0));
	if(RD_MEM_32(MAILBOX_IRQSTATUS_CLR_U0)&(0x1<<22))
    {
	//	printk("MAILBOX_MSGSTATUS1 0x%x\n",RD_MEM_32(MAILBOX_MSGSTATUS1));
		while(RD_MEM_32(MAILBOX_MSGSTATUS1))
    	{
    		message = RD_MEM_32(MAILBOX_MESSAGE1);
	//		printk("IRQ message=0x%x\n",message);
    	}
    	WR_MEM_32(MAILBOX_IRQSTATUS_CLR_U0,(0x1<<22));
		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_PRCM_BASE_ADDR  = ioremap_nocache(PRCM_BASE_ADDR,0x2000);
	//printk("v_PRCM_BASE_ADDR 0x%x\n",RD_MEM_32(v_PRCM_BASE_ADDR));
	
	v_CTRL_MODULE_BASE_ADDR = ioremap_nocache(CTRL_MODULE_BASE_ADDR,0x1000);
	//printk("v_CTRL_MODULE_BASE_ADDR 0x%x\n",RD_MEM_32(v_CTRL_MODULE_BASE_ADDR));	
	
    v_GEM_L2RAM_BASE_ADDR = ioremap_nocache(GEM_L2RAM_BASE_ADDR,0x1000);
	
	v_DUCATI_BASE_ADDR = ioremap_nocache(0x55020000,0x10000);
		
	v_DUCATI_MMU_CFG = ioremap_nocache(0x55080000,0x3000);
			

	CM_GEM_CLKSTCTRL	=	(v_PRCM_BASE_ADDR + 0x0400) ;
	CM_ACTIVE_GEM_CLKCTRL	=	(v_PRCM_BASE_ADDR + 0x0420);
	PM_ACTIVE_PWRSTST	=	(v_PRCM_BASE_ADDR + 0x0A04);
	RM_ACTIVE_RSTCTRL	=	(v_PRCM_BASE_ADDR + 0x0A10) ;
	RM_ACTIVE_RSTST	=		(v_PRCM_BASE_ADDR + 0x0A14) ;

	CM_MMU_CLKSTCTRL	=	(v_PRCM_BASE_ADDR + 0x140C) ;
	CM_ALWON_MMUDATA_CLKCTRL =	(v_PRCM_BASE_ADDR + 0x159C) ;

	CM_MMUCFG_CLKSTCTRL 	=	(v_PRCM_BASE_ADDR + 0x1410) ;
	CM_ALWON_MMUCFG_CLKCTRL  =	(v_PRCM_BASE_ADDR + 0x15A8) ;
	DSPBOOTADDR	=		(v_CTRL_MODULE_BASE_ADDR + 0x0048) ;
	DSPMEM_SLEEP	=		(v_CTRL_MODULE_BASE_ADDR + 0x0650) ;

	v_MAILBOX_BASE_ADDR = ioremap_nocache(MAILBOX_BASE_ADDR,0x1000);
	//printk("v_MAILBOX_BASE_ADDR 0x%x\n",v_MAILBOX_BASE_ADDR);	
	MAILBOX_MESSAGE = v_MAILBOX_BASE_ADDR+0x40+0x4*10;
	MAILBOX_FIFOSTATUS  = v_MAILBOX_BASE_ADDR+0x80+0x4*10;
	MAILBOX_MSGSTATUS = v_MAILBOX_BASE_ADDR+0xc0+0x4*10;
	MAILBOX_IRQSTATUS_RAW_U1 = v_MAILBOX_BASE_ADDR+0x100+0x10*1;
	MAILBOX_IRQSTATUS_CLR_U1 = v_MAILBOX_BASE_ADDR+0x104+0x10*1;
	MAILBOX_IRQENABLE_SET_U1 = v_MAILBOX_BASE_ADDR+0x108+0x10*1;
	MAILBOX_IRQENABLE_CLR_U1 = v_MAILBOX_BASE_ADDR+0x10c+0x10*1;

	MAILBOX_MESSAGE1 = v_MAILBOX_BASE_ADDR+0x40+0x4*11;
	MAILBOX_FIFOSTATUS1  = v_MAILBOX_BASE_ADDR+0x80+0x4*11;
	MAILBOX_MSGSTATUS1 = v_MAILBOX_BASE_ADDR+0xc0+0x4*11;
	MAILBOX_IRQSTATUS_RAW_U0 = v_MAILBOX_BASE_ADDR+0x100+0x10*0;
	MAILBOX_IRQSTATUS_CLR_U0 = v_MAILBOX_BASE_ADDR+0x104+0x10*0;
	MAILBOX_IRQENABLE_SET_U0 = v_MAILBOX_BASE_ADDR+0x108+0x10*0;
	MAILBOX_IRQENABLE_CLR_U0 = v_MAILBOX_BASE_ADDR+0x10c+0x10*0;
	
	MAILBOX_MESSAGE_M3 = v_MAILBOX_BASE_ADDR+0x40+0x4*9;
	MAILBOX_FIFOSTATUS_M3  = v_MAILBOX_BASE_ADDR+0x80+0x4*9;
	MAILBOX_MSGSTATUS_M3 = v_MAILBOX_BASE_ADDR+0xc0+0x4*9;
	MAILBOX_IRQSTATUS_RAW_U2 = v_MAILBOX_BASE_ADDR+0x100+0x10*2;
	MAILBOX_IRQSTATUS_CLR_U2 = v_MAILBOX_BASE_ADDR+0x104+0x10*2;
	MAILBOX_IRQENABLE_SET_U2 = v_MAILBOX_BASE_ADDR+0x108+0x10*2;
	MAILBOX_IRQENABLE_CLR_U2 = v_MAILBOX_BASE_ADDR+0x10c+0x10*2;
	
	RM_DEFAULT_RSTCTRL = v_PRCM_BASE_ADDR+0x0B10;
	CM_DEFAULT_DUCATI_CLKSTCTRL = v_PRCM_BASE_ADDR+0x0518;
	CM_DEFAULT_DUCATI_CLKCTRL = v_PRCM_BASE_ADDR+0x0574;
	DUCATI_BASE_ADDR = v_DUCATI_BASE_ADDR;
	DUCATI_MMU_CFG = v_DUCATI_MMU_CFG;
	RM_DEFAULT_RSTST = v_PRCM_BASE_ADDR+0x0B14;
	
	printk(KERN_ALERT "insmod dsp mailbox driver\n");
	DSPWake();
	result = request_irq(77, mbox_interrupt, IRQF_SHARED,"dsp_mailbox", mailbox_devp);
	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(77, mailbox_devp);
	return result;	
}

static void mailbox_exit(void)
{
	int i;
	free_irq(77, mailbox_devp);
	iounmap(v_PRCM_BASE_ADDR);
	iounmap(v_CTRL_MODULE_BASE_ADDR);
	iounmap(v_GEM_L2RAM_BASE_ADDR);
	iounmap(v_DUCATI_BASE_ADDR);
	iounmap(v_DUCATI_MMU_CFG);
	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);
