/* * drivers/misc/ce_mailbox.c * * GPL LICENSE SUMMARY * * Copyright(c) 2015-2017 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * The full GNU General Public License is included in this distribution * in the file called LICENSE.GPL. * * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(CONFIG_AVM_EVENTNODE_PUMA7) #include #endif #include static struct proc_dir_entry *mbx_proc_dir; static struct npcpu_appcpu_mbx_user mbx_usr_info; struct npcpu_rpc_info rpc_info; bool glob_mbx_is_initialized = false; #define NPCPU_IP_START_BYTE 0 #define APPCPU_IP_START_BYTE 4 #define NETMASK_START_BYTE 8 #define VLAN_ID_START_BYTE 12 #define MAX_NO_BYTE 4 #define BYTE_SIZE 8 //#define AVM_MBOX_DBG #ifdef AVM_MBOX_DBG # define DPRINTK(fmt, args...) printk("CE_MAILBOX[%s:%d]: " fmt "\n", __FUNCTION__, __LINE__, ## args) #else # define DPRINTK(fmt, args...) do{ } while(0) #endif //#define CE_MAILBOX_INTERRUPT AVALANCHE_CPU_SW_INT //static DEFINE_SPINLOCK(ce_mailbox_lock); /* Guards the hardware */ static DECLARE_COMPLETION(cmd_done); /* delay macro for future use.*/ //#define MBX_MAX_POLLING_DELAY (msecs_to_jiffies(30000)) /* Translate IP address */ #define NIPQUAD(addr) \ ((unsigned char*)&addr)[0], \ ((unsigned char*)&addr)[1], \ ((unsigned char*)&addr)[2], \ ((unsigned char*)&addr)[3] static long lastNpCpuEventAvmEventLegacy=0; static long lastCpuConnectEventLegacy=0; static irqreturn_t arm2atom_interrupt(int cause); static unsigned int ref = 0; static void ce_mailbox_send_reply_ack(void) { int ret = 0; ret = hwMbox_sendAckOpcode(HW_MBOX_MASTER_NP_CPU); if(ret) { printk(KERN_ERR "CE_MAILBOX: Failed to send Ack to msg ret %d\n", ret); } return; } static unsigned int get_4bytevalue_fromIndex( Uint8 *dataBuf, int index){ unsigned int ret = 0; unsigned int loopcount = 0; Uint8 *databuff = dataBuf; for (; loopcount < MAX_NO_BYTE; loopcount++){ ret = ((ret<eventId); switch(eventId) { case NPCPU_EVENT_AVM_CPU_CONNECT: DPRINTK("NPCPU_EVENT_AVM_CPU_CONNECT"); lastCpuConnectEventLegacy=be32_to_cpu((receivedEvent)->paramPtr); ret=0; break; case NPCPU_EVENT_AVMEVENT: DPRINTK("NPCPU_EVENT_AVMEVENT"); lastNpCpuEventAvmEventLegacy=be32_to_cpu((receivedEvent)->paramPtr); ret=0; break; case NPCPU_EVENT_TFFS_UPDATED: printk("received NPCPU_EVENT_TFFS_UPDATED, not considered at the moment"); //clrMask |= eventId; ret=0; break; case NPCPU_EVENT_GPIO_INIT_EXIT: case NPCPU_EVENT_SPI_INIT_EXIT: case NPCPU_EVENT_EMMC_INIT_EXIT: case NPCPU_EVENT_EMMC_ADVANCE_INIT_EXIT: case NPCPU_EVENT_KERNEL_STARTED: case NPCPU_EVENT_URLADER_UPDATE: case NPCPU_EVENT_URLADER_RESTART: case BOOTCFG_REG_SW_INT1_CLR: ret=0; break; case BOOTCFG_REG_SW_INT1_SET: //DPRINTK("received SW interrupt from ARM, param=%x",be32_to_cpu((receivedEvent)->paramPtr)); cause=le32_to_cpu((receivedEvent)->paramPtr); arm2atom_interrupt(cause); ret=0; break; default: printk( "ERROR : receive_specific_event_notification Event=0x%x is invalid \n", eventId ); } if(ret==0) ce_mailbox_send_reply_ack(); return ret; } static int npcpu_ip_proc_show(struct seq_file *m, void *v) { seq_printf(m, "%u.%u.%u.%u\n",NIPQUAD(mbx_usr_info.parameter.npcpu_ipv4_addr)); return 0; } static int appcpu_ip_proc_show(struct seq_file *m, void *v) { seq_printf(m, "%u.%u.%u.%u\n",NIPQUAD(mbx_usr_info.parameter.appcpu_ipv4_addr)); return 0; } static int netmask_proc_show(struct seq_file *m, void *v) { seq_printf(m, "%u.%u.%u.%u\n",NIPQUAD(mbx_usr_info.parameter.netmask)); return 0; } static int vlan_proc_show(struct seq_file *m, void *v) { seq_printf(m, "%u\n",mbx_usr_info.parameter.vlan_id); return 0; } static int npcpu_ip_proc_open(struct inode *inode, struct file *file) { return single_open(file, npcpu_ip_proc_show, PDE_DATA(inode)); } static int appcpu_ip_proc_open(struct inode *inode, struct file *file) { return single_open(file, appcpu_ip_proc_show, PDE_DATA(inode)); } static int netmask_proc_open(struct inode *inode, struct file *file) { return single_open(file, netmask_proc_show, PDE_DATA(inode)); } static int vlan_proc_open(struct inode *inode, struct file *file) { return single_open(file, vlan_proc_show, PDE_DATA(inode)); } static int mbx_open(struct inode *inode, struct file *filp) { printk(KERN_DEBUG "CE_MAILBOX: npcpu_appcpu_mbx driver open ref %d\n", ++ref); return 0; } static int mbx_close(struct inode *inode, struct file *filp) { printk(KERN_DEBUG "CE_MAILBOX: npcpu_appcpu_mbx driver close ref %d\n", --ref); return 0; } /* Polloing on specivec callback from mailbox - max wait forever to get RPC info from ARM */ static int mbx_wait_till_ready(void) { int res = wait_for_completion_interruptible(&cmd_done); if (res) printk("CE_MAILBOX: %s:%u: interrupted \n", __func__, __LINE__ ); return res; } /* * npcpu_appcpu_mbx_receive_callback * * Wait for callback from the NPCPU - indicates NPCPU got this event * polling the APPCPU Mbx ce_mailbox_receive_msg - DO not wait forever exit on timeout * Returns 0 if success, negative if error / timeout */ long npcpu_appcpu_mbx_receive_specific_callback(void) { if (!glob_mbx_is_initialized) { printk("CE_MAILBOX: ERROR : Intel(R) CE Mailbox driver is not installed \n"); return -ENODEV; } if (mbx_wait_till_ready()) { printk("CE_MAILBOX: APPCPU-NPCPU MBX is stuck - ce_mailbox_receive_msg from NPCPU is NOT arrived\n" ); return -ENOSYS; } return 0; } static long mbx_unlocked_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { int err = 0; /* * Extract the type and number bitfields, and don't decode * wrong cmds */ if (_IOC_TYPE(cmd) != MBX_MODULE_ID) return -ENOTTY; if (_IOC_NR(cmd) > MBX_IOC_MAXNR) return -ENOTTY; /* * Verify the user space pointer */ 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; /* copy from the user the event ID and if need to copy parameter */ if ( ( arg != 0 ) && (copy_from_user(&mbx_usr_info, (struct npcpu_appcpu_mbx_user *)arg, sizeof(struct npcpu_appcpu_mbx_user)) )) { return -EFAULT; } switch (cmd) { case MBX_GET_EVENT_CMD: /*register callbacks for RPC messages */ hwMbox_registerRecvOpcode(HW_MBOX_MASTER_NP_CPU, ce_mailbox_receive_msg, NPCPU_APPCPU_HW_MBOX_TAG_RPC, 0x1); err = npcpu_appcpu_mbx_receive_specific_callback(); if(mbx_usr_info.isParamRequired) { if (arg && copy_to_user((struct npcpu_appcpu_mbx_user *)arg, &mbx_usr_info, sizeof(struct npcpu_appcpu_mbx_user))) return -EFAULT; } break; case MBX_SEND_EVENT_CMD: default: printk(KERN_ERR "CE_MAILBOX: NPCPU/APPCPU Mailbox driver receive Wrong IOCTL command = 0x%x \n",cmd); return -EFAULT; break; } return err; } static int sendDataOverHwMailBox(Uint8 *dataBuf,Uint32 dataLen){ int replyLen=4; int ret; ret = hwMbox_sendOpcode(HW_MBOX_MASTER_NP_CPU, NPCPU_APPCPU_HW_MBOX_TAG_EVENT_NODE, dataBuf, dataLen,sizeof(hwMbox_opcodeMessage_t), &replyLen); return ret; } /* * npcpu_appcpu_mbx_send_notification * @eventId: Event need send to NPCPU * @ParamPtr - pointer to parameter for this event (optional) * * Send Specific event notification to the NPCPU. * If Parameter is requiered update the corresponding parameter * Returns 0 if success, negative if error / timeout */ long npcpu_appcpu_mbx_send_notification(unsigned short eventID, unsigned int *paramPtr) { int ret; EventOverHWMbox_t *mboxBuffer; int len = sizeof(EventOverHWMbox_t) + sizeof(hwMbox_opcodeMessage_t); if (!glob_mbx_is_initialized || hwMbox_isReady()) return -ENODEV; mboxBuffer = kzalloc(len, GFP_ATOMIC); mboxBuffer->eventId = eventID; mboxBuffer->paramPtr =(unsigned int *) *paramPtr; DPRINTK("sending event no=%i, param=0x%x",mboxBuffer->eventId,(unsigned int) mboxBuffer->paramPtr); ret = sendDataOverHwMailBox((Uint8 *)mboxBuffer, len); if (ret) { pr_err("CE_MAILBOX: Failed to send cmd to arm %d\n", ret); if (ret == -ENODEV) { /* netip down? */ pr_err("HW-MAILBOX down, uninitialize ARM-ATOM Interface for Avm-Events\n"); glob_mbx_is_initialized = false; } } //ret == 0 => received ack => ARM read buffer kfree(mboxBuffer); return ret; } int npcpu_appcpu_mbx_ready(void) { return glob_mbx_is_initialized == true; } static int npcpu_appcpu_mbx_event_notification(unsigned short eventId, unsigned int *param, unsigned int check_only) { switch(eventId) { case NPCPU_EVENT_AVM_CPU_CONNECT: if(lastCpuConnectEventLegacy==0) break; DPRINTK("last NPCPU_EVENT_AVM_CPU_CONNECT=0x%x",(unsigned int)lastCpuConnectEventLegacy); *param = lastCpuConnectEventLegacy; lastCpuConnectEventLegacy=0; return 0; break; case NPCPU_EVENT_AVMEVENT: if (lastNpCpuEventAvmEventLegacy==0) break; DPRINTK("last NPCPU_EVENT_AVMEVENT=0x%x",(unsigned int)lastNpCpuEventAvmEventLegacy); *param=lastNpCpuEventAvmEventLegacy; lastNpCpuEventAvmEventLegacy=0; return 0; break; default: printk( "ERROR : receive_specific_event_notification Event=0x%x is invalid", eventId ); return -ENOSYS; } return -EAGAIN; } /* * npcpu_appcpu_mbx_receive_event_notification * @eventId: Wait for this event * param - if the event requiered parameter - this is the output parameter * * Wait for specific event from the NPCPU - * polling the NPCPU Mbx structure - DO not wait forever exit on timeout * Returns 0 if success, negative if error / timeout */ long npcpu_appcpu_mbx_receive_event_notification(unsigned short eventId, unsigned int *param) { return npcpu_appcpu_mbx_event_notification(eventId, param, 0); } /* * npcpu_appcpu_mbx_check_event_notification * @eventId: Check for this event * param - if the event requiered parameter - this is the output parameter * * Check if specific event has been sent by the NPCPU without waiting for it - * * Returns: 0 event has been received * -EAGAIN event has not been received * negative on error / timeout */ long npcpu_appcpu_mbx_check_event_notification(unsigned short eventId, unsigned int *param) { return npcpu_appcpu_mbx_event_notification(eventId, param, 1); } static struct file_operations mbx_fops = { .owner = THIS_MODULE, .unlocked_ioctl = mbx_unlocked_ioctl, .open = mbx_open, .release = mbx_close, }; static struct file_operations npcpu_ip_fops = { .owner = THIS_MODULE, .open = npcpu_ip_proc_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, }; static struct file_operations appcpu_ip_fops = { .owner = THIS_MODULE, .open = appcpu_ip_proc_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, }; static struct file_operations netmask_fops = { .owner = THIS_MODULE, .open = netmask_proc_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, }; static struct file_operations vlan_fops = { .owner = THIS_MODULE, .open = vlan_proc_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, }; static int remove_mbx_proc(struct proc_dir_entry *mbx_dir) { if (!mbx_dir) return -EIO; remove_proc_entry(CE_MAILBOX_DEVICE_NAME, mbx_dir); remove_proc_entry("npcpu_ip", mbx_dir); remove_proc_entry("appcpu_ip", mbx_dir); remove_proc_entry("netmask", mbx_dir); remove_proc_entry("vlan_id", mbx_dir); remove_proc_entry(CE_MAILBOX_DEVICE_NAME,NULL); return 0; } static struct proc_dir_entry * __init create_mbx_proc(void) { /* create /proc/ce_mailbox */ struct proc_dir_entry *mbx_dir = proc_mkdir(CE_MAILBOX_DEVICE_NAME, NULL); if (!mbx_dir) return NULL; /* create /proc/ce_mailbox/ce_mailbox */ if (!proc_create_data(CE_MAILBOX_DEVICE_NAME, S_IRUSR|S_IWUSR | S_IRGRP |S_IWGRP |S_IROTH |S_IWOTH, mbx_dir, &mbx_fops, NULL)) return NULL; /* create /proc/ce_mailbox/npcpu_ipv4_addr */ if (!proc_create_data("npcpu_ip", S_IRUSR|S_IRGRP|S_IROTH, mbx_dir, &npcpu_ip_fops, NULL)) return NULL; /* create /proc/ce_mailbox/appcpu_ipv4_addr */ if (!proc_create_data("appcpu_ip", S_IRUSR|S_IRGRP|S_IROTH, mbx_dir, &appcpu_ip_fops, NULL)) return NULL; /* create /proc/ce_mailbox/network_mask */ if (!proc_create_data("netmask", S_IRUSR|S_IRGRP|S_IROTH, mbx_dir, &netmask_fops, NULL)) return NULL; /* create /proc/ce_mailbox/vlan_id */ if (!proc_create_data("vlan_id", S_IRUSR|S_IRGRP|S_IROTH, mbx_dir, &vlan_fops, NULL)) return NULL; return mbx_dir; } /* Init the module */ static void argv_cleanup(struct subprocess_info *info) { argv_free(info->argv); } static irqreturn_t arm2atom_interrupt(int cause) { int ret = -ENOMEM; DPRINTK("handling interrupt from ARM..."); for(;;) { // read ARM2ATOM IRQ flags //cause = npcpu_bootcfg_ctrl_read_reg(BOOTCFG_REG_SW_INT1_STAT); cause &= BOOTCFG_REG_SW_INT1_ARM11_2_ATOM_MASK; if(cause == 0) { break; } /* * reboot ISR flag will be cleard by shut down function to signal ARM that shut down * is in progress */ if (cause & BOOTCFG_REG_SW_INT1_ARM11_2_ATOM_REBOOT_ISR) { int argc; char **argv = argv_split(GFP_ATOMIC, "/sbin/reboot", &argc); static char *envp[] = {"HOME=/", "PATH=/sbin:/bin:/usr/sbin:/usr/bin", NULL}; struct subprocess_info *info; printk(KERN_CRIT "Got reboot interrupt from ARM. Shutting down the system now\n"); info = call_usermodehelper_setup(argv[0], argv, envp, GFP_ATOMIC,NULL,argv_cleanup, NULL ); if (info == NULL) { argv_free(argv); } else { //call_usermodehelper_setfns(info, NULL, argv_cleanup, NULL); ret = call_usermodehelper_exec(info, UMH_NO_WAIT); } if ((info == NULL) || ret) { printk(KERN_ERR "Failed to reboot: forcing orderly_poweroff\n"); orderly_poweroff(true); } cause ^= BOOTCFG_REG_SW_INT1_ARM11_2_ATOM_REBOOT_ISR; } // all other ISR flags can be cleared right away //npcpu_bootcfg_ctrl_write_reg(BOOTCFG_REG_SW_INT1_CLR, cause); #if defined(CONFIG_AVM_EVENTNODE_PUMA6) || defined(CONFIG_AVM_EVENTNODE_PUMA7) cause ^= handle_remotecpuirq(cause); #endif/*--- #if defined(CONFIG_AVM_EVENTNODE_PUMA6) ---*/ if(cause != 0){ pr_err("Got unknown interrupt from ARM %x\n", cause); } } return IRQ_RETVAL(IRQ_HANDLED); } static int ce_mailbox_init(void) { //int ret; mbx_proc_dir = create_mbx_proc(); if (!mbx_proc_dir) printk("CE_MAILBOX: ERROR - %s initialization- can not create proc fs\n",CE_MAILBOX_DEVICE_NAME); glob_mbx_is_initialized = true; printk(KERN_INFO "CE_MAILBOX: Initializing Intel(R) NPCPU <-> APPCPU CE Mailbox Device Driver\n"); hwMbox_registerRecvOpcode(HW_MBOX_MASTER_NP_CPU, ce_mailbox_receive_event, NPCPU_APPCPU_HW_MBOX_TAG_EVENT_NODE, 0 ); DPRINTK(KERN_INFO "CE_MAILBOX: registerd for events from NP CPU"); //if((ret=request_irq(CE_MAILBOX_INTERRUPT, arm2atom_interrupt, IRQF_DISABLED | IRQF_TRIGGER_RISING, CE_MAILBOX_DEVICE_NAME, NULL))) { // printk("[%s]Unable to get IRQ #%d for device named %s error %d\n", __func__,CE_MAILBOX_INTERRUPT, CE_MAILBOX_DEVICE_NAME,ret); // return -ENOMEM; //} //printk(KERN_INFO"enable IRQ #%d for device named %s\n", CE_MAILBOX_INTERRUPT, CE_MAILBOX_DEVICE_NAME); return 0; } static void __exit ce_mailbox_exit(void) { remove_mbx_proc(mbx_proc_dir); glob_mbx_is_initialized = false; printk(KERN_INFO "CE_MAILBOX: Intel(R) NPCPU <-> APPCPU CE Mailbox Device Driver removed\n"); } static_notifier_module_init(hwmbox_ready, ce_mailbox_init); module_exit(ce_mailbox_exit); MODULE_AUTHOR("Intel Corporation"); MODULE_DESCRIPTION("Intel(R) CE mailbox exchange between ARM and ATOM"); MODULE_LICENSE("GPL v2");