| /******************************************************************************* |
| * Copyright (C) 2013, ZTE Corporation. |
| * |
| * File Name: icp.c |
| * File Mark: |
| * Description: |
| * Others: |
| * Version: V0.1 |
| * Author: ShiDeYou |
| * Date: 2013-3-13 |
| * History 1: |
| * Date: |
| * Version: |
| * Author: |
| * Modification: |
| * History 2: |
| ******************************************************************************/ |
| |
| /******************************************************************************* |
| * Include files * |
| *******************************************************************************/ |
| #include <linux/clk.h> |
| #include <linux/err.h> |
| #include <linux/platform_device.h> |
| #include <linux/io.h> |
| #include <linux/slab.h> |
| #include <linux/delay.h> |
| #include <linux/pm_runtime.h> |
| #include <mach/irqs.h> |
| #include <mach/iomap.h> |
| #include <asm/mach/map.h> |
| #include <mach/rpmsg.h> |
| #include <linux/kthread.h> |
| |
| |
| /******************************************************************************* |
| * Macro * |
| *******************************************************************************/ |
| |
| |
| /******************************************************************************* |
| * Global Variable * |
| *******************************************************************************/ |
| |
| static Icp_rpmsg_drv icp_arm0_info = { |
| .name = "icp_arm0", |
| .Channel_config = |
| { |
| .ChConfig = {CH_IRAM, CH_IRAM, CH_DDR, CH_DDR, CH_DDR, CH_DDR,CH_DDR,CH_DDR,\ |
| CH_DDR, CH_DDR, CH_DDR, CH_DDR, CH_DDR, CH_DDR, CH_DDR,CH_DDR,\ |
| CH_DDR, CH_DDR, CH_DDR, CH_DDR, CH_DDR, CH_DDR, CH_DDR,CH_DDR,\ |
| CH_DDR, CH_DDR, CH_DDR, CH_DDR, CH_DDR, CH_DDR, CH_DDR,CH_DDR,\ |
| CH_DDR, CH_DDR, CH_DDR, CH_DDR, CH_DDR, CH_DDR, CH_DDR,CH_DDR,\ |
| CH_DDR}, |
| }, |
| }; |
| |
| static Icp_rpmsg_drv icp_m0_info = { |
| .name = "icp_m0", |
| .Channel_config = |
| { |
| .ChConfig = {CH_IRAM, CH_IRAM, CH_IRAM, CH_IRAM, CH_IRAM, CH_IRAM,CH_IRAM,\ |
| CH_IRAM, CH_IRAM, CH_IRAM, CH_IRAM, CH_IRAM, CH_IRAM, CH_IRAM,\ |
| CH_IRAM, CH_IRAM, CH_IRAM, CH_IRAM, CH_IRAM, CH_IRAM,CH_IRAM,\ |
| CH_IRAM, CH_IRAM, CH_IRAM, CH_IRAM, CH_IRAM, CH_IRAM,CH_IRAM,\ |
| CH_IRAM, CH_IRAM, CH_IRAM, CH_IRAM, CH_IRAM, CH_IRAM,CH_IRAM, |
| CH_IRAM, CH_IRAM, CH_IRAM, CH_IRAM, CH_IRAM, CH_IRAM}, |
| }, |
| }; |
| |
| Icp_rpmsg_drv *IcpRpMsgInfo[] = { &icp_m0_info, &icp_arm0_info, NULL }; |
| |
| T_HalRpMsg_RWP ap2cp_chinfo[ACTOR_MAXID][CHANNEL_MAXID]; |
| T_HalRpMsg_RWP cp2ap_chinfo[ACTOR_MAXID][CHANNEL_MAXID]; |
| |
| /******************************************************************************* |
| * Function: zx29_Icp_SetInt |
| * Description: This function is used for generating icp interrupt to inform remote cpu; |
| * Parameters: |
| * Input: |
| actorID: id of remote cpu |
| chID: id of channel |
| * Output:None |
| * |
| * Returns:None |
| * |
| * |
| * Others: |
| ********************************************************************************/ |
| int icp_set_int(T_ZDrvRpMsg_ActorID actorID, T_ZDrvRpMsg_ChID chID) |
| { |
| T_HalRpMsg_ChInfo *channel_send = IcpRpMsgInfo[actorID]->ChInfo_Send_Base + chID; |
| |
| if(!(channel_send->flag & CHANNEL_FLAG)) |
| return RPMSG_CHANNEL_INEXISTANCE; |
| |
| if(chID<32) |
| { |
| iowrite32((1<<chID), &(IcpRpMsgInfo[actorID]->IcpReg->control.low_word)) ; |
| } |
| else |
| { |
| iowrite32((1<<(chID - 32)),&(IcpRpMsgInfo[actorID]->IcpReg->control.high_word)); |
| } |
| return RPMSG_SUCCESS; |
| } |
| |
| /******************************************************************************* |
| * Function: zx29_Icp_ClearInt |
| * Description: This function is used for clear icp interrupt from remote cpu; |
| * Parameters: |
| * Input: |
| actorID: id of remote cpu |
| chID: id of channel |
| * Output:None |
| * |
| * Returns:None |
| * |
| * |
| * Others: |
| ********************************************************************************/ |
| void icp_clear_int(T_ZDrvRpMsg_ActorID actorID, T_ZDrvRpMsg_ChID chID) |
| { |
| if(chID<32) |
| { |
| iowrite32((1<<chID), &(IcpRpMsgInfo[actorID]->IcpReg->clear.low_word)); |
| } |
| else |
| { |
| iowrite32(1<<(chID - 32), &(IcpRpMsgInfo[actorID]->IcpReg->clear.high_word)) ; |
| } |
| } |
| |
| /******************************************************************************* |
| * Function: zx29_Icp_GetInt |
| * Description: This function is used for get icp interrupt from remote cpu; |
| * Parameters: |
| * Input: |
| * actorID: id of remote cpu |
| * chID: id of channel |
| * Output:None |
| * |
| * Returns:None |
| * |
| * |
| * Others: |
| ********************************************************************************/ |
| T_HalIcp_Dword icp_get_int(T_ZDrvRpMsg_ActorID actorID) |
| { |
| void __iomem *tmp = NULL; |
| T_HalIcp_Dword IcpState = {0}; |
| tmp = &(IcpRpMsgInfo[actorID]->IcpReg->state.high_word); |
| IcpState.high_word = ioread32(tmp); |
| tmp = &(IcpRpMsgInfo[actorID]->IcpReg->state.low_word); |
| IcpState.low_word = ioread32(tmp); |
| |
| return IcpState; |
| } |
| |
| /******************************************************************************* |
| * Function: zx29_Icp_GetIntState |
| * Description: This function is used for get the state of icp interruptting of remote cpu; |
| * Parameters: |
| * Input: |
| actorID: id of remote cpu |
| chID: id of channel |
| * Output:None |
| * |
| * Returns:None |
| * |
| * |
| * Others: |
| ********************************************************************************/ |
| int icp_get_intstate(T_ZDrvRpMsg_ActorID actorID, T_ZDrvRpMsg_ChID chID) |
| { |
| if(chID < 32) |
| { |
| if(ioread32(&(IcpRpMsgInfo[actorID]->IcpReg->in_state.low_word)) & (0x1 << chID)) |
| return TRUE; |
| else |
| return FALSE; |
| } |
| else |
| { |
| if(ioread32(&(IcpRpMsgInfo[actorID]->IcpReg->in_state.high_word)) & (0x1 << (chID - 32))) |
| return TRUE; |
| else |
| return FALSE; |
| } |
| } |
| |
| /******************************************************************************* |
| * Function: zx29_Icp_Mask |
| * Description: This function is used for Mask interrupt of channel; |
| * Parameters: |
| * Input: |
| * Output: |
| * |
| * Returns: NONE |
| * |
| * |
| * Others: |
| ********************************************************************************/ |
| void icp_mask_int(T_ZDrvRpMsg_ActorID actorID, T_ZDrvRpMsg_ChID chID) |
| { |
| if(chID < 32) |
| { |
| IcpRpMsgInfo[actorID]->IcpReg->mask.low_word |=(0x1<<chID); |
| } |
| else |
| { |
| IcpRpMsgInfo[actorID]->IcpReg->mask.high_word |= (0x1<<(chID-32)); |
| } |
| } |
| |
| /******************************************************************************* |
| * Function: zx29_Icp_UnMask |
| * Description: This function is used for unmask interrupt of channel; |
| * Parameters: |
| * Input: |
| * Output: |
| * |
| * Returns: |
| * NONE |
| * |
| * |
| * Others: |
| ********************************************************************************/ |
| void icp_unmask_int(T_ZDrvRpMsg_ActorID actorID, T_ZDrvRpMsg_ChID chID) |
| { |
| if(chID < 32) |
| { |
| IcpRpMsgInfo[actorID]->IcpReg->mask.low_word &= ~(0x1<<chID); |
| } |
| else |
| { |
| IcpRpMsgInfo[actorID]->IcpReg->mask.high_word &= ~(0x1<<(chID-32)); |
| } |
| |
| } |
| |
| static icp_operations icp_ops = { |
| .Icp_SetInt = icp_set_int, |
| .Icp_ClearInt = icp_clear_int, |
| .Icp_GetInt = icp_get_int, |
| .Icp_GetIntState = icp_get_intstate, |
| .Icp_Mask = icp_mask_int, |
| .Icp_UnMask = icp_unmask_int, |
| }; |
| |
| |
| |
| |
| static int __devinit icp_probe(struct platform_device *pdev) |
| { |
| struct resource *icp_mem; |
| struct resource *icp_iram_send; |
| struct resource *icp_iram_recv; |
| struct resource *icp_ddr_send; |
| struct resource *icp_ddr_recv; |
| |
| int ret; |
| Icp_rpmsg_drv *list; |
| unsigned int i; |
| |
| list = IcpRpMsgInfo[pdev->id]; |
| list->actorID = pdev->id; |
| |
| list->intline = platform_get_irq(pdev, 0 ); |
| |
| icp_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, "icp"); |
| icp_iram_send = platform_get_resource_byname(pdev, IORESOURCE_MEM, "icp_iram_send"); |
| icp_iram_recv = platform_get_resource_byname(pdev, IORESOURCE_MEM, "icp_iram_recv"); |
| icp_ddr_send = platform_get_resource_byname(pdev, IORESOURCE_MEM, "icp_ddr_send"); |
| icp_ddr_recv = platform_get_resource_byname(pdev, IORESOURCE_MEM, "icp_ddr_recv"); |
| |
| if (!icp_mem || !icp_iram_send || !icp_iram_recv) |
| return -ENOMEM; |
| |
| list->IcpReg = (T_HalIcp_Reg *)ioremap(icp_mem->start, resource_size(icp_mem)); |
| list->ChInfo_Send_Base = (T_HalRpMsg_ChInfo *)ioremap(icp_iram_send->start, resource_size(icp_iram_send)); |
| list->ChInfo_Recv_Base = (T_HalRpMsg_ChInfo *)ioremap(icp_iram_recv->start, resource_size(icp_iram_recv)); |
| list->ChInfo_SendBase_offset= (unsigned int)(list->ChInfo_Send_Base) - (unsigned int)(icp_iram_send->start); |
| list->ChInfo_RecvBase_offset= (unsigned int)(list->ChInfo_Recv_Base) - (unsigned int)(icp_iram_recv->start); |
| |
| memset(list->ChInfo_Send_Base, 0, (CHANNEL_MAXID * sizeof(T_HalRpMsg_ChInfo))); |
| |
| list->Ddr_SendBase = (unsigned int)(icp_ddr_send->start); |
| list->Ddr_RecvBase = (unsigned int)(icp_ddr_recv->start); |
| list->Ddr_SendBase_offset = (unsigned int)ioremap(icp_ddr_send->start, resource_size(icp_ddr_send)) - list->Ddr_SendBase; |
| list->Ddr_RecvBase_offset = (unsigned int)ioremap(icp_ddr_recv->start, resource_size(icp_ddr_recv)) - list->Ddr_RecvBase; |
| |
| list->IcpReg->mask.high_word=0xffffffff; |
| list->IcpReg->mask.low_word=0xffffffff; |
| |
| list->Channel_config.CurIramAddr = (unsigned int)(icp_iram_send->start + CHANNEL_MAXID * sizeof(T_HalRpMsg_ChInfo)); |
| list->Channel_config.CurIramSpace_Size = icp_iram_send->end - icp_iram_send->start - CHANNEL_MAXID * sizeof(T_HalRpMsg_ChInfo); |
| list->Channel_config.CurDdrAddr = (unsigned int)(icp_ddr_send->start); |
| list->Channel_config.CurDdrSpace_Size = icp_ddr_send->end - icp_ddr_send->start; |
| |
| if (!list->IcpReg && !list->Channel_config.CurIramAddr && !list->Channel_config.CurDdrAddr) |
| return -ENODEV; |
| |
| list->ops = &icp_ops; |
| |
| for(i = 0; i<CHANNEL_MAXID; i++) |
| { |
| ap2cp_chinfo[pdev->id][i].writep = &(((T_HalRpMsg_ChInfo *)(list->ChInfo_Send_Base + i))->SendPos); |
| ap2cp_chinfo[pdev->id][i].readp = &(((T_HalRpMsg_ChInfo *)(list->ChInfo_Recv_Base + i))->RecvPos); |
| ap2cp_chinfo[pdev->id][i].endp = &(((T_HalRpMsg_ChInfo *)(list->ChInfo_Send_Base + i))->EndAddr); |
| cp2ap_chinfo[pdev->id][i].writep = &(((T_HalRpMsg_ChInfo *)(list->ChInfo_Recv_Base + i))->SendPos); |
| cp2ap_chinfo[pdev->id][i].readp = &(((T_HalRpMsg_ChInfo *)(list->ChInfo_Send_Base + i))->RecvPos); |
| cp2ap_chinfo[pdev->id][i].endp = &(((T_HalRpMsg_ChInfo *)(list->ChInfo_Recv_Base + i))->EndAddr); |
| } |
| |
| ret = icp_rpmsg_register(list); |
| |
| if (ret) |
| goto unmap_base; |
| |
| printk("rpmsg: icp_probe actorID = %d, success!\n", list->actorID); |
| |
| return 0; |
| |
| unmap_base: |
| iounmap(list->IcpReg); |
| iounmap(list->ChInfo_Send_Base); |
| iounmap(list->ChInfo_Recv_Base); |
| //iounmap((unsigned int *)(list->Ddr_SendBase_map)); |
| //iounmap((unsigned int *)(list->Ddr_RecvBase_map)); |
| |
| |
| |
| return ret; |
| } |
| |
| static int __devexit icp_remove(struct platform_device *pdev) |
| { |
| int ret = 0; |
| ret = icp_rpmsg_unregister(IcpRpMsgInfo[pdev->id]); |
| return ret; |
| } |
| |
| static struct platform_driver icp_driver = { |
| .probe = icp_probe, |
| .remove = __devexit_p(icp_remove), |
| .driver = { |
| .name = "icp", |
| }, |
| }; |
| |
| static int __init icp_init(void) |
| { |
| return platform_driver_register(&icp_driver); |
| } |
| |
| subsys_initcall(icp_init); |
| |
| static void __exit icp_exit(void) |
| { |
| platform_driver_unregister(&icp_driver); |
| } |
| |
| module_exit(icp_exit); |
| |
| |