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