/* * kernel/arm_atom_mbx.c * * Copyright (C) 2020-2021 MaxLinear, Inc. * Copyright (C) 2012-2020 Intel Corporation * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License, version 2, 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, see . * * SPDX-License-Identifier: GPL-2.0-only */ #include #include #include #include #include #include #include #include #include #include #include #include #include "arm_atom_mbx.h" #include "sram_api.h" #include #ifdef CONFIG_MACH_PUMA7 #include #include "asm-arm/arch-avalanche/puma7/hw_mbox/hw_mbox_drv.h" #include "asm-arm/arch-avalanche/puma7/hw_mbox/hw_mbox_defs.h" #include "puma7_npcpu_appcpu_defs.h" #include "asm-arm/arch-avalanche/puma7/puma7.h" #include #endif #ifdef CONFIG_MACH_PUMA6 #include #endif #include #include #if defined(CONFIG_AVM_EVENTNODE_PUMA6) || defined(CONFIG_AVM_EVENTNODE_PUMA7) #include #endif/*--- #if defined(CONFIG_AVM_EVENTNODE_PUMA6) ---*/ #define ARM_MBX_STRUCT_BASE_ADDR GET_SRAM_ADDR(MAILBOX_SECTION_OFFSET) //#define ARM_MBX_STRUCT_BASE_ADDR GET_SRAM_ADDR(EVENT_MAILBOX_SECTION_OFFSET) #define ATOM_MBX_STRUCT_BASE_ADDR (ARM_MBX_STRUCT_BASE_ADDR + 0x00000004) #define ARM_MBX_STRUCT_EVENT_MASK_ADDR (ARM_MBX_STRUCT_BASE_ADDR) #define ARM_MBX_STRUCT_ACK_MASK_ADDR (ARM_MBX_STRUCT_BASE_ADDR + 2) #define ATOM_MBX_STRUCT_EVENT_MASK_ADDR (ATOM_MBX_STRUCT_BASE_ADDR) #define ATOM_MBX_STRUCT_ACK_MASK_ADDR (ATOM_MBX_STRUCT_BASE_ADDR + 2) /* RPC-IF INFO */ /* Data format in this address would be in LE byte order */ #define NPCPU_RPC_IPV4_ADDR (ARM_MBX_STRUCT_BASE_ADDR + 0x8) #define APPCPU_RPC_IPV4_ADDR (ARM_MBX_STRUCT_BASE_ADDR + 0xC) #define RPC_NETWORK_MASK (ARM_MBX_STRUCT_BASE_ADDR + 0x10) #define VLAN_ID (ARM_MBX_STRUCT_BASE_ADDR + 0x14) // AVM extensions #define LED_DATA (ARM_MBX_STRUCT_BASE_ADDR + 0x18) #define AVMEVENT_DATA (ARM_MBX_STRUCT_BASE_ADDR + 0x1C) //#define AVM_MBOX_DBG #ifdef AVM_MBOX_DBG # define DPRINTK(fmt, args...) printk("ARM_ATOM_MBX[%s:%d]: " fmt "\n", __FUNCTION__, __LINE__, ## args) #else # define DPRINTK(fmt, args...) no_printk(fmt, ## args) #endif /* 3 seconds timeout */ #define MBX_DEFAULT_TIMEOUT 3 //#define AVALANCHE_ATOM2ARM_INT AVALANCHE_CPU_SW_INT #define reg_write_32(addr, data) (( *(volatile unsigned int *) (addr) ) = (data)) #define reg_read_32(addr) ( *(volatile unsigned int *) (addr) ) #define reg_write_16(addr, data) (( *(volatile unsigned short *) (addr) ) = (data)) #define reg_read_16(addr) ( *(volatile unsigned short *) (addr) ) static unsigned int ref = 0; static int mbx_Major; static int atomIsReady=0; static long lastAtomEventLedLegacy=0; static long lastAtomEventAVMEventLegacy=0; static long lastAtomEventCpuConnectLegacy=0; static struct class *mbx_class; static irqreturn_t atom2arm_interrupt(int cause); /* Polloing on specivec ACK/Event bit - max timeout is 3 sec */ static int mbx_wait_till_ready(volatile unsigned int regAddressToPoll, unsigned short eventId, unsigned int timeout) { unsigned long jiffies_timeout; while (timeout !=0) { jiffies_timeout = jiffies + msecs_to_jiffies(1000); /* 1 second */ do{ if( (le16_to_cpu( reg_read_16(regAddressToPoll) ) & eventId) == 0 ) { continue; } else { return 0; /* Normal exit */ } }while(time_after(jiffies_timeout, jiffies)); timeout--; if ((timeout !=0 ) && ((timeout % MBX_DEFAULT_TIMEOUT) == 0)) { printk("NP/APP MBX: waiting...\n" ); } } printk("NP/APP MBX: Timeout on wait till ready.\n" ); return 1; } static int mbx_open(struct inode *inode, struct file *filp) { printk(KERN_INFO "NP/APP MBX: Driver open ref %d\n", ++ref); return 0; } static int mbx_close(struct inode *inode, struct file *filp) { printk(KERN_INFO "NP/APP MBX: Driver close ref %d\n", --ref); return 0; } static long mbx_unlocked_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { struct arm11_atom_mbx_user mbx_usr_info = {0}; int err = 0; /* copy from the user the event ID and if need to copy parameter */ if (arg) { if (copy_from_user(&mbx_usr_info, (struct arm11_atom_mbx_user *)arg, sizeof(struct arm11_atom_mbx_user)) ) { return -EFAULT; } } switch (cmd) { case MBX_SEND_EVENT_CMD: { err = arm_atom_mbx_send_notification_over_hw_mbox(mbx_usr_info.eventId,(unsigned int *)&mbx_usr_info.parameter); break; } case MBX_GET_EVENT_CMD: { err = arm_atom_mbx_receive_event_notification(mbx_usr_info.eventId,(unsigned int *)&mbx_usr_info.parameter,MBX_DEFAULT_TIMEOUT); if(mbx_usr_info.isParamRequired) { if (copy_to_user(&(((struct arm11_atom_mbx_user *)arg)->parameter ), &mbx_usr_info.parameter, sizeof(mbx_usr_info.parameter) )) { return -EFAULT; } } break; } case MBX_SEND_ACK_CMD: { err = arm_atom_mbx_send_ack(mbx_usr_info.eventId); break; } case MBX_RECEIVE_ACK_CMD: { err = arm_atom_mbx_receive_specific_ack(mbx_usr_info.eventId); break; } default: { printk(KERN_ERR "NP/APP MBX: Error - receive Wrong IOCTL command = 0x%x \n",cmd); return -EFAULT; } } return err; } int sendDataOverHwMailBox(Uint8 *dataBuf,Uint32 dataLen){ int replyLen=4; return hwMbox_sendOpcode(HW_MBOX_MASTER_APP_CPU, NPCPU_APPCPU_HW_MBOX_TAG_EVENT_NODE, dataBuf, dataLen,sizeof(hwMbox_opcodeMessage_t), &replyLen); } /*arm_atom_mbx_send_notification avm extension for puma7*/ long arm_atom_mbx_send_notification_over_hw_mbox(unsigned short eventID, unsigned int *paramPtr) { int ret=-1; EventOverHWMbox_t *mboxBuffer; int len = sizeof(EventOverHWMbox_t) + sizeof(hwMbox_opcodeMessage_t); if(!atomIsReady){ DPRINTK("failed, waiting for (first) event msg from atom..."); return -EBUSY; } /* Take the HW mutex */ if (hw_mutex_lock_interruptible(HW_MUTEX_ARM_MBX) != 0) { DPRINTK("arm_atom_mbx_send_notification- Can't lock HW mutex"); return -ENOSYS; } mboxBuffer = kzalloc(len, GFP_KERNEL); mboxBuffer->eventId = eventID; mboxBuffer->paramPtr = (unsigned int *) *paramPtr; DPRINTK("sending event no=0x%x, param=%p",mboxBuffer->eventId,mboxBuffer->paramPtr); switch(eventID){ //case NPCPU_EVENT_RPC_IF_OBTAIN_ADDR: // break; case ARM11_EVENT_AVM_CPU_CONNECT: case ARM11_EVENT_AVMEVENT: case ARM11_EVENT_GPIO_INIT_EXIT: //no need for param v case ARM11_EVENT_SPI_INIT_EXIT: case ARM11_EVENT_EMMC_INIT_EXIT: case ARM11_EVENT_EMMC_ADVANCE_INIT_EXIT: case ARM11_MBX_ATOM_RESET_SYNC: case ARM11_EVENT_KERNEL_STARTED: case ARM11_EVENT_URLADER_UPDATE: case ARM11_EVENT_URLADER_RESTART: case ARM11_EVENT_TFFS_UPDATED: case BOOTCFG_REG_SW_INT1_SET: ret = sendDataOverHwMailBox((Uint8 *)mboxBuffer, len); break; default: { DPRINTK( "ERROR : arm_atom_mbx_send_notification Event=0x%x is invalid", eventID ); ret= -ENOSYS; } } if(ret) printk(KERN_ERR "[%s]: Failed to send cmd to atom !%d\n", __func__,ret); kfree(mboxBuffer); hw_mutex_unlock(HW_MUTEX_ARM_MBX); return ret; } /* * arm_atom_mbx_send_notification * @eventId: Event need send to ATOM * @ParamPtr - pointer to parameter for this event (optional) * * Send Specific event notification to the ATOM. * If Parameter is requiered update the corresponding parameter (in specific address of ARM SRAM) * Returns 0 if success, negative if error / timeout */ long arm_atom_mbx_send_notification(unsigned short eventID, unsigned int *paramPtr) { volatile unsigned short eventMask; /* Take the HW mutex */ if (hw_mutex_lock_interruptible(HW_MUTEX_ARM_MBX) != 0) { printk("NP/APP MBX: Error - Can't lock HW mutex\n"); return -ENOSYS; } /* Get the event mask */ eventMask = le16_to_cpu( reg_read_16(ARM_MBX_STRUCT_EVENT_MASK_ADDR) ); eventMask |= eventID; switch(eventID) { case NPCPU_EVENT_RPC_IF_OBTAIN_ADDR: { struct npcpu_rpc_info *rpc_info = (struct npcpu_rpc_info *)paramPtr; BUG_ON(!rpc_info); DPRINTK("sending ipv4 adress.."); reg_write_32( NPCPU_RPC_IPV4_ADDR, (rpc_info->npcpu_ipv4_addr) ); reg_write_32( APPCPU_RPC_IPV4_ADDR,(rpc_info->appcpu_ipv4_addr) ); reg_write_32( RPC_NETWORK_MASK, (rpc_info->netmask) ); reg_write_32( VLAN_ID, cpu_to_le32(rpc_info->vlan_id) ); break; } case ARM11_EVENT_GPIO_INIT_EXIT: case ARM11_EVENT_SPI_INIT_EXIT: case ARM11_EVENT_EMMC_INIT_EXIT: case ARM11_EVENT_EMMC_ADVANCE_INIT_EXIT: case ARM11_MBX_ATOM_RESET_SYNC: { /* Event w/o parameters */ break; } default: { /* Release the HW Mutex */ hw_mutex_unlock(HW_MUTEX_ARM_MBX); printk( "NP/APP MBX: Error - Event=0x%x is invalid \n", eventID ); return -ENOSYS; } } /* set the appropiated bit of the event mask on ARM structur */ reg_write_16(ARM_MBX_STRUCT_EVENT_MASK_ADDR, cpu_to_le16(eventMask)); /* Release the HW Mutex */ hw_mutex_unlock(HW_MUTEX_ARM_MBX); return 0; } /* * arm_atom_mbx_send_ack * @eventId: Event need to ACK on * * Send ACK to ATOM on specific event received by ARM * Set the apropriate ACK bit in the ATOM mbox struct indicates ARM was processing this event * Returns 0 if success, negative if error / timeout */ long arm_atom_mbx_send_ack(unsigned short eventID) { volatile unsigned short ackMask; /* Take the HW mutex */ if (hw_mutex_lock_interruptible(HW_MUTEX_ATOM_MBX) != 0) { printk(KERN_ERR"NP/APP MBX: Error - Can't lock HW mutex\n"); return -ENOSYS; } ackMask = le16_to_cpu( reg_read_16(ATOM_MBX_STRUCT_ACK_MASK_ADDR) ); printk("NP/APP MBX: Ack Mask = 0x%x\n",ackMask); ackMask |= eventID; /* Set the ACK bit on ATOM ACK bit mask structure*/ reg_write_16(ATOM_MBX_STRUCT_ACK_MASK_ADDR,cpu_to_le16(ackMask)); printk("NP/APP MBX: Writing ack Mask = 0x%x for event = 0x%x\n",ackMask,eventID); /* Release the HW Mutex */ hw_mutex_unlock(HW_MUTEX_ATOM_MBX); return 0; } /* * arm_atom_mbx_receive_specific_ack * @eventId: Wait for ack on Event Sent earlier by the ARM * * Wait for specific ACK from the ATOM - indicates ATOM got this event * polling the ARM Mbx structure (ACK bit Mask)- DO not wait forever exit on timeout * Returns 0 if success, negative if error / timeout */ long arm_atom_mbx_receive_specific_ack(unsigned short eventId) { volatile unsigned short ackMask; if( mbx_wait_till_ready(ARM_MBX_STRUCT_ACK_MASK_ADDR, eventId, MBX_DEFAULT_TIMEOUT) ) { printk("NP/APP MBX: Timeout on ACK from ATOM on eventId=0x%x NOT arrived \n", eventId ); return -ENOSYS; } printk("NP/APP MBX: GOT ACK from ATOM on eventId=0x%x arrived \n", eventId ); /* ACK was received - Need to Clear the event and ACK bit*/ /* Take the HW mutex */ if (hw_mutex_lock_interruptible(HW_MUTEX_ARM_MBX) != 0) { printk(KERN_ERR"NP/APP MBX: Error - Can't lock HW mutex\n"); return -ENOSYS; } printk("NP/APP MBX: Clear the ACK Vector from ATOM on eventId=0x%x \n", eventId ); /* Clear ARM ACK vector */ ackMask = le16_to_cpu( reg_read_16(ARM_MBX_STRUCT_ACK_MASK_ADDR) ); ackMask = (ackMask & ~(eventId)); reg_write_16(ARM_MBX_STRUCT_ACK_MASK_ADDR,cpu_to_le16(ackMask)); /* Release the HW Mutex */ hw_mutex_unlock(HW_MUTEX_ARM_MBX); return 0; } /* * arm_atom_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 ATOM - * polling the ATOM Mbx structure - DO not wait forever exit on timeout * Returns 0 if success, negative if error / timeout */ long arm_atom_mbx_receive_event_notification(unsigned short eventId, unsigned int *param, unsigned int timeout) { volatile unsigned short ackMask; bool ackRequiered = 0; if( mbx_wait_till_ready(ATOM_MBX_STRUCT_EVENT_MASK_ADDR, eventId,timeout) ) { printk( "NP/APP MBX - Wait for Event=0x%x from APP-CPU fail on timeout \n", eventId ); return -ENOSYS; } switch(eventId) { case ATOM_EVENT_SPI_ADVANCE_EXIT: case ATOM_EVENT_EMMC_ADVANCE_EXIT: { break; } default: { printk(KERN_ERR"NP/APP MBX: Error - receive_specific_event_notification Event=0x%x is invalid \n", eventId ); return -ENOSYS; } } // printk( "receive_specific_event_notification Event=0x%x parameter = 0x%x \n", eventId,(param==NULL?'0':*param) ); if (ackRequiered) { /* Take the HW mutex */ if (hw_mutex_lock_interruptible(HW_MUTEX_ATOM_MBX) != 0) { printk(KERN_ERR"NP/APP MBX: Error - arm_atom_mbx_receive_event_notification- Can't lock HW mutex\n"); return -ENOSYS; } ackMask = le16_to_cpu(reg_read_16(ATOM_MBX_STRUCT_ACK_MASK_ADDR)) | eventId ; reg_write_16(ATOM_MBX_STRUCT_ACK_MASK_ADDR,cpu_to_le16(ackMask)); /* Set the appropiated ACK bit */ /* Release the HW Mutex */ hw_mutex_unlock(HW_MUTEX_ATOM_MBX); } return 0; } static int arm_atom_hw_mbox_callback(hw_mbox_Masters_e commander, Uint8 *dataBuf, Uint32 dataLen, Uint32 *token){ int eventId, cause; EventOverHWMbox_t* receivedEvent= (EventOverHWMbox_t *) dataBuf; DPRINTK("new Event msg from Atom"); eventId = le16_to_cpu((receivedEvent)->eventId); switch(eventId) { case ATOM_EVENT_LED: DPRINTK("ATOM_EVENT_LED"); lastAtomEventLedLegacy=be32_to_cpu((receivedEvent)->paramPtr); break; case ATOM_EVENT_AVM_CPU_CONNECT: DPRINTK("ATOM_EVENT_AVM_CPU_CONNECT"); lastAtomEventCpuConnectLegacy=be32_to_cpu((receivedEvent)->paramPtr); break; case ATOM_EVENT_AVMEVENT: DPRINTK("ATOM_EVENT_AVMEVENT"); lastAtomEventAVMEventLegacy=be32_to_cpu((receivedEvent)->paramPtr); atomIsReady=1; break; case ATOM_EVENT_SPI_ADVANCE_EXIT: case ATOM_EVENT_EMMC_ADVANCE_EXIT: case BOOTCFG_REG_SW_INT_CLR: break; case BOOTCFG_REG_SW_INT_SET: cause = be32_to_cpu((receivedEvent)->paramPtr); DPRINTK("received SW interrupt from Atom, param=%x",cause); atom2arm_interrupt(cause); break; default: DPRINTK( "ERROR : receive_specific_event_notification Event=0x%x is invalid", eventId ); return -ENOSYS; } hwMbox_sendAckOpcode(HW_MBOX_MASTER_APP_CPU); return 0; } //notification_nonblock Verhalten für Puma7. Die Nachrichten wurden bereits mit der callback Methode empfangen long arm_atom_mbx_get_Last_Message_of_Event(unsigned short eventId, unsigned int *param){ switch(eventId) { case ATOM_EVENT_LED: DPRINTK("last ATOM_EVENT_LED=0x%lx",lastAtomEventLedLegacy); *param=lastAtomEventLedLegacy; return 0; break; case ATOM_EVENT_AVM_CPU_CONNECT: DPRINTK("last ATOM_EVENT_AVM_CPU_CONNECT=0x%lx",lastAtomEventCpuConnectLegacy); *param=lastAtomEventCpuConnectLegacy; return 0; break; case ATOM_EVENT_AVMEVENT: DPRINTK("last ATOM_EVENT_AVMEVENT=0x%lx",lastAtomEventAVMEventLegacy); *param=lastAtomEventAVMEventLegacy; return 0; break; case ATOM_EVENT_SPI_ADVANCE_EXIT: case ATOM_EVENT_EMMC_ADVANCE_EXIT: break; default: { DPRINTK("ERROR : receive_specific_event_notification Event=0x%x is invalid", eventId ); } } return -EAGAIN; } /* * arm_atom_mbx_get_arm_reg * Returns Arm mailbox register */ unsigned int arm_atom_mbx_get_arm_reg(void) { return le16_to_cpu( reg_read_16(ARM_MBX_STRUCT_EVENT_MASK_ADDR) ); } static struct file_operations mbx_fops = { .owner = THIS_MODULE, .unlocked_ioctl = mbx_unlocked_ioctl, .open = mbx_open, .release = mbx_close, }; static void argv_cleanup(struct subprocess_info *info) { argv_free(info->argv); } static irqreturn_t atom2arm_interrupt(int cause) { int ret = -ENOMEM; //int cause; DPRINTK("handling interrupt from ATOM ..."); for(;;) { //cause = PAL_sysBootCfgCtrl_ReadReg(BOOTCFG_REG_SW_INT_STAT); cause &= BOOTCFG_REG_SW_INT_ATOM_2_ARM11_INTC_MASK; if(cause == 0) { break; } /* * reboot ISR flag will be cleard by shut down function to signal Atom that shut down * is in progress */ if (cause & BOOTCFG_REG_SW_INT_ATOM_2_ARM11_INTC_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 ATOM. Shutting down the system now\n"); /*info = call_usermodehelper_setup(argv[0], argv, envp, GFP_ATOMIC); if (info == NULL) { argv_free(argv); } else { call_usermodehelper_setfns(info, NULL, argv_cleanup, NULL); ret = call_usermodehelper_exec(info, UMH_NO_WAIT); } */ 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_INT_ATOM_2_ARM11_INTC_REBOOT_ISR; } // all other ISR flags can be cleared right away //PAL_sysBootCfgCtrl_WriteReg(BOOTCFG_REG_SW_INT_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){ printk(KERN_ERR "Got unknown interrupt from ATOM %x\n", cause); } } return IRQ_RETVAL(IRQ_HANDLED); } /* Init the module */ static int __init arm_atom_mbx_init(void) { struct device *mbx_dev; printk(KERN_INFO "NP/APP MBX: Initializing Intel(R) NP-CPU/APP-CPU MailBox driver\n"); // register mbx as a device mbx_Major = register_chrdev(0, DEVICE_NAME, &mbx_fops); if (mbx_Major < 0) { printk(KERN_ERR "NP/APP MBX: Error - Failed to register device %s with error %d\n", DEVICE_NAME, mbx_Major); return mbx_Major; } /* create /dev/arm_atom_mbx * we use udev to make the file */ mbx_class = class_create(THIS_MODULE,DEVICE_NAME); if (IS_ERR(mbx_class)) { printk(KERN_ERR "NP/APP MBX: Error - Failed to create class %s\n", DEVICE_NAME); unregister_chrdev(mbx_Major, DEVICE_NAME); return PTR_ERR(mbx_class); } mbx_dev = device_create(mbx_class, NULL, MKDEV(mbx_Major,0),NULL,DEVICE_NAME); if (IS_ERR(mbx_dev)) { printk(KERN_ERR "NP/APP MBX: Error - Failed to create device %s\n", DEVICE_NAME); class_destroy(mbx_class); unregister_chrdev(mbx_Major, DEVICE_NAME); return PTR_ERR(mbx_dev); } printk(KERN_INFO "NP/APP MBX: Create device: /dev/%s\n",DEVICE_NAME); /* HW Mailbox checking and registeration */ if ( 0 != hwMbox_isReady() ) { printk(KERN_ERR"HW Mailbox driver not ready, init fails!"); return -1; } printk("HW Mailbox driver is ready\n"); /* HW Mailbox is ready, registering the callback */ if(0 != hwMbox_registerRecvOpcode(HW_MBOX_MASTER_APP_CPU, arm_atom_hw_mbox_callback, NPCPU_APPCPU_HW_MBOX_TAG_EVENT_NODE, 0 )) { printk("hwMbox_registerRecvOpcode fails, Master = %d, Tag = %d", HW_MBOX_MASTER_APP_CPU, NPCPU_APPCPU_HW_MBOX_TAG_EVENT_NODE); return -1; } /* if(request_threaded_irq(AVALANCHE_ATOM2ARM_INT, NULL, atom2arm_interrupt, IRQF_DISABLED | IRQF_TRIGGER_RISING, DEVICE_NAME, mbx_dev)) { printk(KERN_ERR "unable to get IRQ #%d for device named %s\n", AVALANCHE_ATOM2ARM_INT, DEVICE_NAME); return -ENOMEM; } printk("enable IRQ #%d for device named %s\n", AVALANCHE_ATOM2ARM_INT, DEVICE_NAME); */ printk(KERN_INFO"ARM ATOM MBX init DONE\n"); return 0; } /* remove the module */ static void __exit arm_atom_mbx_exit(void) { device_destroy(mbx_class,MKDEV(mbx_Major,0)); class_unregister(mbx_class); class_destroy(mbx_class); unregister_chrdev(mbx_Major, DEVICE_NAME); } subsys_initcall_sync(arm_atom_mbx_init); module_exit(arm_atom_mbx_exit); EXPORT_SYMBOL(arm_atom_mbx_send_notification); EXPORT_SYMBOL(arm_atom_mbx_send_notification_over_hw_mbox); EXPORT_SYMBOL(arm_atom_mbx_send_ack); EXPORT_SYMBOL(arm_atom_mbx_receive_specific_ack); EXPORT_SYMBOL(arm_atom_mbx_receive_event_notification); EXPORT_SYMBOL(arm_atom_mbx_get_Last_Message_of_Event); MODULE_DESCRIPTION("NP/APP Event Mailbox Device Driver"); MODULE_LICENSE("GPL");