/****************************************************************************** Copyright (c) 2010 Lantiq Deutschland GmbH Am Campeon 3; 85579 Neubiberg, Germany For licensing information, see the file 'LICENSE' in the root folder of this software module. ******************************************************************************/ #define LINUX_2_6 #ifdef LINUX_2_6 #include #ifndef UTS_RELEASE #include #endif /* UTC_RELEASE */ #endif /* LINUX_2_6 */ #ifndef AUTOCONF_INCLUDED #include #endif /* AUTOCONF_INCLUDED */ #include #include #include #include #include #include #include #include #include #include "ifxmips_pmcu.h" /** \defgroup LQ_PMCU_PRG Program Guide \ingroup LQ_PMCU
"Power Management Control Unit ( PMCU )" .
\section Purpose Purpose The PMCU is the central unit of the power management framework. It is responsible for processing requests to change the power states of the hardware modules whose device drivers have registered with it. The PMCU ensures a smooth power state transition by resolving all dependencies before finally changing to the requested power state.\n\n\ Supported Power States:
D0 on State
D1 standby
D2 low power standby
D3 off State
\n \n "High Level PMCU Architecture" . \image html cpe_power_sw_architecture2.jpg \n \n \section Function_Calltree Function Calltree of PMCU \image html pmcucalltree.gif \n \section Registration_Example Example for Registration to the PMCU All modules/drivers/subsystems which contribute to power saving features or/and are effected by these must register to the PMCU. For example if the feature frequency change is getting active, most of the subsystems are effected because of reduced CPU performance and reduced DDR bandwidth. In case a subsystem is registered to the PMCU, the subsysten may deny a frequency change if it needs full performance of CPU and DDR bandwidth at this time. If the subsystem is idle the PMCU should be informed to check if the entire system can run with a reduced frequency. \n The following steps must be done to register to the pmcu:\n 1. fill the registry structure IFX_PMCU_REGISTER_t pmcuRegister; as shown in the example below.\n 2. define the registrated callback functions with the module specific functionality.\n 3. call the registration function ifx_pmcu_register ( &pmcuRegister ); with a defined registry structure during the initialisation phase of the module\n 4. all modules where no dependency list is defined (see list below), define a NULL pointer instead.\n \n Registration Example:\n \code //define dependency list; state D0D3 means don't care //static declaration is necessary to let gcc accept this static initialisation. //The dependency list shows the pmcu module which module specific power state correspond //to the actual requested power state. static IFX_PMCU_MODULE_DEP_t depListWlan= { 1, { {IFX_PMCU_MODULE_CPU, IFX_PMCU_STATE_D0, IFX_PMCU_STATE_D0D3, IFX_PMCU_STATE_D0D3, IFX_PMCU_STATE_D0D3} } }; : // This functions returns the current power state of the module static IFX_PMCU_RETURN_t ifx_wlan_stateGet(IFX_PMCU_STATE_t *pmcuModState) { printk(KERN_DEBUG "ifx_wlan_stateGet is called\n"); if(wlanPwrFea_g == 1){ printk(KERN_DEBUG "current power state of WLAN module is D0\n"); *pmcuModState = IFX_PMCU_STATE_D0; // set here the right value } if(wlanPwrFea_g == 0){ printk(KERN_DEBUG "power feature of WLAN module is disabled\n"); *pmcuModState = IFX_PMCU_STATE_INVALID; // must be set to INVALID } return IFX_PMCU_RETURN_SUCCESS; } // The function should be used to enable/disable the module specific power saving methods static IFX_PMCU_RETURN_t ifx_wlan_pwrFeatureSwitch(IFX_PMCU_PWR_STATE_ENA_t pmcuPwrStateEna) { if (pmcuPwrStateEna == IFX_PMCU_PWR_STATE_ON) { wlanPwrFea_g = 1; printk(KERN_DEBUG "All WLAN power saving features are switched on\n"); return IFX_PMCU_RETURN_SUCCESS; } if (pmcuPwrStateEna == IFX_PMCU_PWR_STATE_OFF) { wlanPwrFea_g = 0; printk(KERN_DEBUG "All WLAN power saving features are switched off\n"); return IFX_PMCU_RETURN_SUCCESS; } return IFX_PMCU_RETURN_SUCCESS; } // This function should be used to do all the necessary clean-up's before a the real // power state change is initiated; e.g. flush all serial buffers inside the UART before // the frequency will be changed. static IFX_PMCU_RETURN_t ifx_wlan_preChange(IFX_PMCU_MODULE_t pmcuModule, IFX_PMCU_STATE_t newState, IFX_PMCU_STATE_t oldState) { printk(KERN_DEBUG "ifx_wlan_preChange is called\n"); return IFX_PMCU_RETURN_SUCCESS; } // This function initiate the real power state change. The module should do all the necessary // adpations to the new state. static IFX_PMCU_RETURN_t ifx_wlan_stateChange(IFX_PMCU_STATE_t newState) { printk(KERN_DEBUG "ifx_wlan_stateChange is called\n"); return IFX_PMCU_RETURN_SUCCESS; } // This function should be used to do all the necessary post processing after a the real // power state change was initiated. static IFX_PMCU_RETURN_t ifx_wlan_postChange(IFX_PMCU_MODULE_t pmcuModule, IFX_PMCU_STATE_t newState, IFX_PMCU_STATE_t oldState) { printk(KERN_DEBUG "ifx_wlan_postChange is called\n"); return IFX_PMCU_RETURN_SUCCESS; } : void ifx_wlan_init(void) { : IFX_PMCU_REGISTER_t pmcuRegister; : memset (&pmcuRegister, 0, sizeof(pmcuRegister)); pmcuRegister.pmcuModule=IFX_PMCU_MODULE_WLAN; pmcuRegister.pmcuModuleNr=0; pmcuRegister.pmcuModuleDep = &depListWlan; pmcuRegister.pre = ifx_wlan_preChange; pmcuRegister.post = ifx_wlan_postChange; pmcuRegister.ifx_pmcu_state_change = ifx_wlan_stateChange; pmcuRegister.ifx_pmcu_state_get = ifx_wlan_stateGet; pmcuRegister.ifx_pmcu_pwr_feature_switch = ifx_wlan_pwrFeatureSwitch; ifx_pmcu_register ( &pmcuRegister ); : } \endcode \section Dependency_Tables Dependency Tables of the subsystems CPU\n \code static IFX_PMCU_MODULE_DEP_t depList= { 10, { {IFX_PMCU_MODULE_VE, 1, IFX_PMCU_STATE_D0D3, IFX_PMCU_STATE_D2, IFX_PMCU_STATE_D2, IFX_PMCU_STATE_D2}, {IFX_PMCU_MODULE_PPE, 0, IFX_PMCU_STATE_D0D3, IFX_PMCU_STATE_D3, IFX_PMCU_STATE_D3, IFX_PMCU_STATE_D3}, {IFX_PMCU_MODULE_USB, 0, IFX_PMCU_STATE_D0D3, IFX_PMCU_STATE_D3, IFX_PMCU_STATE_D3, IFX_PMCU_STATE_D3}, {IFX_PMCU_MODULE_USB, 1, IFX_PMCU_STATE_D0D3, IFX_PMCU_STATE_D3, IFX_PMCU_STATE_D3, IFX_PMCU_STATE_D3}, {IFX_PMCU_MODULE_DSL, 0, IFX_PMCU_STATE_D0D3, IFX_PMCU_STATE_D3, IFX_PMCU_STATE_D3, IFX_PMCU_STATE_D3}, {IFX_PMCU_MODULE_WLAN, 0, IFX_PMCU_STATE_D0D3, IFX_PMCU_STATE_D3, IFX_PMCU_STATE_D3, IFX_PMCU_STATE_D3}, {IFX_PMCU_MODULE_DECT, 0, IFX_PMCU_STATE_D0D3, IFX_PMCU_STATE_D3, IFX_PMCU_STATE_D3, IFX_PMCU_STATE_D3}, {IFX_PMCU_MODULE_GPTC, 0, IFX_PMCU_STATE_D0, IFX_PMCU_STATE_D1, IFX_PMCU_STATE_D2, IFX_PMCU_STATE_D3}, {IFX_PMCU_MODULE_SPI, 0, IFX_PMCU_STATE_D0, IFX_PMCU_STATE_D1, IFX_PMCU_STATE_D2, IFX_PMCU_STATE_D3}, {IFX_PMCU_MODULE_UART, 0, IFX_PMCU_STATE_D0, IFX_PMCU_STATE_D1, IFX_PMCU_STATE_D2, IFX_PMCU_STATE_D3} } }; \endcode VE\n \code static IFX_PMCU_MODULE_DEP_t depList= { 1, { {IFX_PMCU_MODULE_CPU, 0, IFX_PMCU_STATE_D0, IFX_PMCU_STATE_D0D3, IFX_PMCU_STATE_D0D3, IFX_PMCU_STATE_D0D3} } }; \endcode PPE\n \code static IFX_PMCU_MODULE_DEP_t depList= { 1, { {IFX_PMCU_MODULE_CPU, 0, IFX_PMCU_STATE_D0, IFX_PMCU_STATE_D0D3, IFX_PMCU_STATE_D0D3, IFX_PMCU_STATE_D0D3} } }; \endcode USB\n \code static IFX_PMCU_MODULE_DEP_t depList= { 1, { {IFX_PMCU_MODULE_CPU, 0, IFX_PMCU_STATE_D0, IFX_PMCU_STATE_D0D3, IFX_PMCU_STATE_D0D3, IFX_PMCU_STATE_D0D3} } }; \endcode DSL\n \code static IFX_PMCU_MODULE_DEP_t depList= { 1, { {IFX_PMCU_MODULE_CPU, 0, IFX_PMCU_STATE_D0, IFX_PMCU_STATE_D0D3, IFX_PMCU_STATE_D0D3, IFX_PMCU_STATE_D0D3} } }; \endcode WLAN\n \code static IFX_PMCU_MODULE_DEP_t depList= { 1, { {IFX_PMCU_MODULE_CPU, 0, IFX_PMCU_STATE_D0, IFX_PMCU_STATE_D0D3, IFX_PMCU_STATE_D0D3, IFX_PMCU_STATE_D0D3} } }; \endcode DECT\n \code static IFX_PMCU_MODULE_DEP_t depList= { 1, { {IFX_PMCU_MODULE_CPU, 0, IFX_PMCU_STATE_D0, IFX_PMCU_STATE_D0D3, IFX_PMCU_STATE_D0D3, IFX_PMCU_STATE_D0D3} } }; \endcode */ /* PMCU driver device name used during OS registration */ #define IFX_PMCU_DEVICE_NAME "ifx_pmcu" /* PMCU driver version */ #define IFX_PMCU_DRV_VERSION "2.1.5.2" /* size of request ringbuffer */ #define IFX_REQ_BUFFER_SIZE 5 /* max number of entries in dependency list +1 represents the entry of the requested module inside the ifx_pmcu_preCall[][] array */ #define IFX_DEPLIST_SIZE (15 + 1) /* max number of dependency entries inclusive the nested dependencies */ #define IFX_PMCU_ACCU_DEPLIST_MAX 15 /* PMCU printk specific's; colored print_message support */ /******************************************************************************/ #define BLINK "\033[31;1m" #define RED "\033[31;1m" #define YELLOW "\033[33;1m" #define GREEN "\033[32;2m" #define BLUE "\033[34;1m" #define CYAN "\033[36;2m" #define DIM "\033[37;1m" #define NORMAL "\033[0m" #define PMCU_EMERG(module,fmt,arg...)\ if(log_level_g >= 0){\ printk(KERN_EMERG RED #module NORMAL ": " fmt,##arg);\ } #define PMCU_ERR(module,fmt,arg...)\ if(log_level_g >= 0){\ printk(KERN_ERR RED #module NORMAL ": " fmt,##arg);\ } #define PMCU_WARNING(module,fmt,arg...)\ if(log_level_g >= 1){\ printk(KERN_WARNING YELLOW #module NORMAL ": " fmt,##arg);\ } #define PMCU_REQ(module,fmt,arg...)\ if(log_level_g >= 2){\ printk(KERN_ERR GREEN #module NORMAL ": " fmt,##arg);\ } #define PMCU_FUNC(module,fmt,arg...)\ if(log_level_g >= 3){\ printk(KERN_ERR GREEN #module NORMAL ": " fmt,##arg);\ } #define PMCU_INFO(module,fmt,arg...)\ if(log_level_g >= 4){\ printk(KERN_INFO GREEN #module NORMAL ": " fmt,##arg);\ } #define PMCU_DEBUG(module,fmt,arg...)\ if(log_level_g >= 5){\ printk(KERN_DEBUG CYAN #module NORMAL ": " fmt,##arg);\ } /******************************************************************************/ #undef PMCU_DEBUG_ENABLE //#define PMCU_DEBUG_ENABLE #ifdef PMCU_DEBUG_ENABLE /* debug macro to print the content of the request ringBuffer */ #define PMCU_DEBUG_PRINT_REQ_BUFFER(max) \ { \ int i; \ for (i=0; i= IFX_REQ_BUFFER_SIZE ) { return IFX_PMCU_RETURN_ERROR; } spin_lock_irqsave(&ifx_pmcu_lock, iflags); /* first check if there is already a request for this module pending. If this is the case, reject the actual one. */ for (i=0;i= IFX_REQ_BUFFER_SIZE ) { ifx_pmcu_reqPutIndex = 0; } ifx_pmcu_reqBufferSize++; spin_unlock_irqrestore(&ifx_pmcu_lock, iflags); //PMCU_DEBUG_PRINT_REQ_BUFFER(5); return IFX_PMCU_RETURN_SUCCESS; } static IFX_PMCU_REQ_STATE_t ifx_pmcu_get_req( IFX_void_t ) { IFX_PMCU_REQ_STATE_t req; PMCU_FUNC(PMCU, "%s is called\n",__FUNCTION__); req.reqId = IFX_PMCU_PENDING_REQ_ERROR; if ( !ifx_pmcu_reqBufferSize ) { return req; } ifx_pmcu_reqBufferSize--; memcpy(&req, &ifx_pmcu_reqBuffer[ ifx_pmcu_reqGetIndex ], sizeof(req)); ifx_pmcu_reqBuffer[ ifx_pmcu_reqGetIndex ].reqId = IFX_PMCU_NO_PENDING_REQ; ifx_pmcu_reqGetIndex++; if ( ifx_pmcu_reqGetIndex >= IFX_REQ_BUFFER_SIZE ) { ifx_pmcu_reqGetIndex = 0; } return req; } /* this function runs in interrupt context */ static IFX_void_t ifx_pmcu_process_req_tasklet(unsigned long data) { IFX_PMCU_REQ_STATE_t reqState; IFX_PMCU_RETURN_t ret; IFX_PMCU_MODULE_STATE_t pmcuModuleState; IFX_int8_t pmcuPreCallIndex = -1; PMCU_FUNC(PMCU, "%s is called\n",__FUNCTION__); while (ifx_pmcu_reqBufferSize > 0) { pmcuPreCallIndex = -1; /* init ifx_pmcu_preCall[][] array */ memset(ifx_pmcu_accuDepList, 0, sizeof(ifx_pmcu_accuDepList)); /* init accumulated dependency list */ memset(ifx_pmcu_preCall, 0, sizeof(ifx_pmcu_preCall)); /* init memory to process modules only once. */ memset(ifx_pmcu_ModMem, 0, sizeof(ifx_pmcu_ModMem)); /* fetch request from ringBuffer */ /* init index of the ifx_pmcu_accuDepList */ ifx_pmcu_indexAccuDepL = 0; reqState = ifx_pmcu_get_req(); if (reqState.reqId == IFX_PMCU_PENDING_REQ_ERROR) { PMCU_ERR(PMCU, "PMCU Request Buffer underflow\n"); return; } if (reqState.reqId == IFX_PMCU_NO_PENDING_REQ) { PMCU_ERR(PMCU, "No valid PMCU Request in buffer, but tasklet is scheduled!!!!!\n"); return; } /* handle PMCU request */ PMCU_INFO(PMCU, "REQ: pmcuModule=%s, pmcuModuleNr=%d, pmcuState=%s\n", ifx_pmcu_mod[reqState.moduleState.pmcuModule], reqState.moduleState.pmcuModuleNr, ifx_pmcu_ps[reqState.moduleState.pmcuState]); /* parse the dependency list and all nested dependencies */ memcpy(&pmcuModuleState, &reqState.moduleState, sizeof(pmcuModuleState)); if (ifx_pmcu_parse_deplist(pmcuModuleState) == IFX_PMCU_RETURN_ERROR) { PMCU_ERR(PMCU, "Error occured during dependencyList parsing\n"); continue;//return; } //PMCU_DEBUG_PRINT_ACCU_DEPLIST(IFX_PMCU_ACCU_DEPLIST_MAX); /* IFX_PMCU_ACCU_DEPLIST_MAX, ifx_pmcu_indexAccuDepL */ //PMCU_DEBUG_PRINT_MOD_MEM(); /* prepare pmcuModuleState for pre step */ /* --> start pre processing by calling preCallBack functions of all affected modules */ ret = ifx_pmcu_prechange(&pmcuPreCallIndex); //PMCU_DEBUG_PRINT_PRECALL(); /* printout after pre processing */ if (ret == IFX_PMCU_RETURN_SUCCESS) { /* --> start processing state change by calling callBack functions of all affected modules */ ret = ifx_pmcu_statechange(pmcuModuleState, &pmcuPreCallIndex); if (ret != IFX_PMCU_RETURN_SUCCESS) { /* error during ifx_pmcu_state_change() is not allowed! Report FatalError to the application*/ PMCU_ERR(PMCU, "Error was returned from ifx_pmcu_state_change()\n"); } //PMCU_DEBUG_PRINT_PRECALL(); /* printout after statechange */ } /* --> start post processing by calling postCallBack functions of all affected modules */ /* if any error occur during pre or statechange processing set new PowerState to old PowerState, to signalize no change */ if (ret != IFX_PMCU_RETURN_SUCCESS) { pmcuModuleState.pmcuState = ifx_pmcu_map[pmcuModuleState.pmcuModule].pmcuOldState[pmcuModuleState.pmcuModuleNr]; //PMCU_DEBUG(PMCU, "Error/Denied was returned from prechange or statechange\n"); } ret = ifx_pmcu_postchange(pmcuModuleState, &pmcuPreCallIndex); if (ret != IFX_PMCU_RETURN_SUCCESS) { /* error during ifx_pmcu_postchange() is not allowed! Report FatalError to the application*/ PMCU_ERR(PMCU, "Error/Denied was returned from ifx_pmcu_postchange()\n"); } //PMCU_DEBUG_PRINT_PRECALL(); /* printout after post processing */ } } IFX_PMCU_RETURN_t ifx_pmcu_state_req (IFX_PMCU_MODULE_t pmcuModule, IFX_uint8_t pmcuModuleNr, IFX_PMCU_STATE_t newState) { IFX_PMCU_REQ_STATE_t reqState; IFX_int32_t ret; PMCU_REQ(PMCU, "%s is called: Module=%s, ModuleNr=%d, State=%s\n",__FUNCTION__,ifx_pmcu_mod[pmcuModule],pmcuModuleNr, ifx_pmcu_ps[newState]); /* check if the acception of the powerstate request is enabled */ if (request_control_g == 0) { PMCU_INFO(PMCU, "The Powerstate request feature is disabled. All requests are rejected\n"); return IFX_PMCU_RETURN_SUCCESS; } reqState.moduleState.pmcuModule = pmcuModule; reqState.moduleState.pmcuModuleNr = pmcuModuleNr; reqState.moduleState.pmcuState = newState; reqState.reqId = IFX_PMCU_NO_PENDING_REQ; /* check if the requested power state is supported for the HW. If this isn't the case request is rejected. Ask the cpu frequency driver if this Powerstate is supported by the HW */ if (ifx_pmcu_map[IFX_PMCU_MODULE_CPU].pmcuRegister[0].pre == NULL) { PMCU_DEBUG(PMCU, "Frequency driver has no valid preCallback defined\n"); return IFX_PMCU_RETURN_DENIED; } ret = ifx_pmcu_map[IFX_PMCU_MODULE_CPU].pmcuRegister[0].pre(IFX_PMCU_MODULE_CPU, newState, IFX_PMCU_STATE_INVALID); if (ret == IFX_PMCU_RETURN_ERROR) { PMCU_DEBUG(PMCU, "Requested Powerstate %s is not supported by HW\n",ifx_pmcu_ps[newState]); return IFX_PMCU_RETURN_DENIED; } /* put new powerState request into request buffer */ if (ifx_pmcu_put_req(reqState) == IFX_PMCU_RETURN_ERROR) { PMCU_ERR(PMCU, "PMCU RequestBuffer overflow !!!\n"); } /* fetch powerState request from requestBuffer with the next interrupt in the system */ tasklet_hi_schedule(&ifx_pmcu_tasklet); return IFX_PMCU_RETURN_SUCCESS; } /** Register a device driver to the PMCU \param [in] pmcuRegister Registration Information \return Returns value as follows: - IFX_SUCCESS: if successful - IFX_ERROR: in case of an error \ingroup IFX_PMCU_KERNEL */ IFX_PMCU_RETURN_t ifx_pmcu_register (IFX_PMCU_REGISTER_t* pmcuRegister) { IFX_PMCU_MAP_t *entry = NULL; IFX_PMCU_RETURN_t retVal = IFX_PMCU_RETURN_ERROR; IFX_PMCU_MODULE_DEP_t *pmcuModuleDep; size_t depSize; IFX_ulong_t iflags; down(&ifx_pmcu_map_mutex); spin_lock_irqsave(&ifx_pmcu_lock, iflags); PMCU_FUNC(PMCU, "%s is called for module %s\n",__FUNCTION__,ifx_pmcu_mod[pmcuRegister->pmcuModule]); entry = &(ifx_pmcu_map[pmcuRegister->pmcuModule]); /* check registry status */ if ( (entry->registryInfo[pmcuRegister->pmcuModuleNr] == IFX_PMCU_MODULE_NOT_REGISTRED) || (entry->registryInfo[pmcuRegister->pmcuModuleNr] == IFX_PMCU_MODULE_UNREGISTERED)) { /* check if at least pre/post callback function is defined */ if ( (pmcuRegister->pre != NULL) && (pmcuRegister->post != NULL) ) { /* copy module registry struct into PMCU structure */ memcpy(&entry->pmcuRegister[pmcuRegister->pmcuModuleNr], pmcuRegister, sizeof(IFX_PMCU_REGISTER_t)); /* check if dependency list is defined */ if (pmcuRegister->pmcuModuleDep == NULL) { PMCU_DEBUG(PMCU, "PMCU/%s registry message: dependency list not defined\n",ifx_pmcu_mod[pmcuRegister->pmcuModule]); /* set registry flag for this module */ entry->registryInfo[pmcuRegister->pmcuModuleNr] = IFX_PMCU_MODULE_REGISTERED; /* as defined the module must have D0 powerState after startup */ entry->pmcuNewState[pmcuRegister->pmcuModuleNr] = IFX_PMCU_STATE_D0; entry->pmcuOldState[pmcuRegister->pmcuModuleNr] = IFX_PMCU_STATE_D0; spin_unlock_irqrestore(&ifx_pmcu_lock, iflags); up(&ifx_pmcu_map_mutex); return IFX_PMCU_RETURN_SUCCESS; } /* copy dependency list into PMCU structure because only a pointer is provided by the module because I do not know the size of the dependency array, I have to allocate memory during runtime*/ /* Start workaround for UGW5.2 only */ // depSize = (sizeof(IFX_PMCU_STATES_DEP_t)) * (pmcuRegister->pmcuModuleDep->nDepth) + (sizeof(pmcuRegister->pmcuModuleDep->nDepth)); switch (pmcuRegister->pmcuModule) { case IFX_PMCU_MODULE_CPU: depSize = (sizeof(IFX_PMCU_STATES_DEP_t)) * (depList_CPU.nDepth) + (sizeof(depList_CPU.nDepth)); pmcuRegister->pmcuModuleDep = &depList_CPU; break; case IFX_PMCU_MODULE_VE: depSize = (sizeof(IFX_PMCU_STATES_DEP_t)) * (depList_VE.nDepth) + (sizeof(depList_VE.nDepth)); pmcuRegister->pmcuModuleDep = &depList_VE; break; default: depSize = (sizeof(IFX_PMCU_STATES_DEP_t)) * (depList_OTH.nDepth) + (sizeof(depList_OTH.nDepth)); pmcuRegister->pmcuModuleDep = &depList_OTH; break; } /* End workaround for UGW5.2 only */ pmcuModuleDep = (IFX_PMCU_MODULE_DEP_t*)kmalloc(depSize, GFP_ATOMIC); /* use atomic kmalloc to be on the save side */ if (pmcuModuleDep == NULL) { /* kmalloc failed */ /* set NULL pointer */ entry->pmcuRegister[pmcuRegister->pmcuModuleNr].pmcuModuleDep = NULL; PMCU_ERR(PMCU, "PMCU/%s memory allocation for dependency list failed\n",entry->pmcuModuleName); spin_unlock_irqrestore(&ifx_pmcu_lock, iflags); up(&ifx_pmcu_map_mutex); return IFX_PMCU_RETURN_ERROR; } //PMCU_DEBUG(PMCU, "PMCU/%s kmalloc for dependency list was successful during registration\n",ifx_pmcu_mod[pmcuRegister->pmcuModule]); /* copy dependency content */ memcpy(pmcuModuleDep, pmcuRegister->pmcuModuleDep, depSize); /* overwrite the dependency pointer from the module in the registry struct with the pointer to the local PMCU copy */ entry->pmcuRegister[pmcuRegister->pmcuModuleNr].pmcuModuleDep = pmcuModuleDep; /* set registry flag for this module */ entry->registryInfo[pmcuRegister->pmcuModuleNr] = IFX_PMCU_MODULE_REGISTERED; /* as defined the module must have D0 powerState after startup */ entry->pmcuNewState[pmcuRegister->pmcuModuleNr] = IFX_PMCU_STATE_D0; entry->pmcuOldState[pmcuRegister->pmcuModuleNr] = IFX_PMCU_STATE_D0; retVal = IFX_PMCU_RETURN_SUCCESS; } else { PMCU_ERR(PMCU, "PMCU/%s registry error: pre/post callback not defined\n",entry->pmcuModuleName); spin_unlock_irqrestore(&ifx_pmcu_lock, iflags); up(&ifx_pmcu_map_mutex); return IFX_PMCU_RETURN_ERROR; } } else { /* module already registered */ retVal = IFX_PMCU_RETURN_DENIED; } spin_unlock_irqrestore(&ifx_pmcu_lock, iflags); up(&ifx_pmcu_map_mutex); return retVal; } /** Unregister a device driver to the PMCU \param [in] pmcuUnregister Unregistration Information. Only module name is required. \return Returns value as follows: - IFX_SUCCESS: if successful - IFX_ERROR: in case of an error \ingroup IFX_PMCU_KERNEL */ IFX_PMCU_RETURN_t ifx_pmcu_unregister (IFX_PMCU_REGISTER_t* pmcuUnregister) { IFX_PMCU_MAP_t *entry = NULL; IFX_ulong_t iflags; down(&ifx_pmcu_map_mutex); spin_lock_irqsave(&ifx_pmcu_lock, iflags); PMCU_FUNC(PMCU, "%s is called for module %s\n",__FUNCTION__,ifx_pmcu_mod[pmcuUnregister->pmcuModule]); entry = &(ifx_pmcu_map[pmcuUnregister->pmcuModule]); if (entry->registryInfo[pmcuUnregister->pmcuModuleNr] == IFX_PMCU_MODULE_REGISTERED ) { /* free dynamic allocated memorey of dependency list */ if (entry->pmcuRegister[pmcuUnregister->pmcuModuleNr].pmcuModuleDep != NULL) { kfree(entry->pmcuRegister[pmcuUnregister->pmcuModuleNr].pmcuModuleDep); //PMCU_DEBUG(PMCU, "PMCU/%s kfree for dependency list was called during unregistration\n",entry->pmcuModuleName); } entry->registryInfo[pmcuUnregister->pmcuModuleNr] = IFX_PMCU_MODULE_UNREGISTERED; entry->pmcuNewState[pmcuUnregister->pmcuModuleNr] = IFX_PMCU_STATE_INVALID; entry->pmcuOldState[pmcuUnregister->pmcuModuleNr] = IFX_PMCU_STATE_INVALID; memset(&entry->pmcuRegister[pmcuUnregister->pmcuModuleNr], 0, sizeof(IFX_PMCU_REGISTER_t)); } spin_unlock_irqrestore(&ifx_pmcu_lock, iflags); up(&ifx_pmcu_map_mutex); return IFX_PMCU_RETURN_SUCCESS; }; static IFX_PMCU_RETURN_t ifx_pmcu_pwr_feature_switch (IFX_PMCU_MODULE_PWR_FEATURE_STATE_t pwrFeaSwitch) { IFX_int32_t ret; IFX_PMCU_MODULE_STATE_t pmcuModState; PMCU_FUNC(PMCU, "%s is called for module %s\n",__FUNCTION__,ifx_pmcu_mod[pwrFeaSwitch.pmcuModule]); /* check if the module supports enable/disable of power feature(s) */ if (ifx_pmcu_map[pwrFeaSwitch.pmcuModule].pmcuRegister[pwrFeaSwitch.pmcuModuleNr].ifx_pmcu_pwr_feature_switch == NULL) { PMCU_ERR(PMCU, "Module %s has no valid Callback defined for pwr_feature_switch\n",ifx_pmcu_mod[pwrFeaSwitch.pmcuModule]); return IFX_PMCU_RETURN_DENIED; } ret = ifx_pmcu_map[pwrFeaSwitch.pmcuModule].pmcuRegister[pwrFeaSwitch.pmcuModuleNr].ifx_pmcu_pwr_feature_switch(pwrFeaSwitch.pmcuPwrFeatureState); if (ret != IFX_PMCU_RETURN_SUCCESS) { PMCU_ERR(PMCU, "Requested Power Feature State is not supported by module %s\n",ifx_pmcu_mod[pwrFeaSwitch.pmcuModule]); return IFX_PMCU_RETURN_DENIED; } else { if (pwrFeaSwitch.pmcuPwrFeatureState == IFX_PMCU_PWR_STATE_ON) { ifx_pmcu_map[pwrFeaSwitch.pmcuModule].dbgFlag &= ~(1 << pwrFeaSwitch.pmcuModuleNr); } else { ifx_pmcu_map[pwrFeaSwitch.pmcuModule].dbgFlag |= 1 << pwrFeaSwitch.pmcuModuleNr; } } /* update power state in pmcu_map[] */ pmcuModState.pmcuModule = pwrFeaSwitch.pmcuModule; pmcuModState.pmcuModuleNr = pwrFeaSwitch.pmcuModuleNr; pmcuModState.pmcuState = IFX_PMCU_STATE_INVALID; ret = ifx_pmcu_get_state(&pmcuModState); if (ret != IFX_PMCU_RETURN_SUCCESS) { PMCU_ERR(PMCU, "Get state function return with error status for module %s\n",ifx_pmcu_mod[pmcuModState.pmcuModule]); return IFX_PMCU_RETURN_ERROR; } else { ifx_pmcu_map[pwrFeaSwitch.pmcuModule].pmcuOldState[pwrFeaSwitch.pmcuModuleNr] = pmcuModState.pmcuState; } return IFX_PMCU_RETURN_SUCCESS; } static IFX_PMCU_RETURN_t ifx_pmcu_get_state (IFX_PMCU_MODULE_STATE_t* pmcuModState) { IFX_int32_t ret; IFX_PMCU_STATE_t pmcuState; PMCU_FUNC(PMCU, "%s is called for module %s\n",__FUNCTION__,ifx_pmcu_mod[pmcuModState->pmcuModule]); /* init powerState */ pmcuModState->pmcuState = IFX_PMCU_STATE_INVALID; /* check if the module is registered. */ if (ifx_pmcu_map[pmcuModState->pmcuModule].registryInfo[pmcuModState->pmcuModuleNr] != IFX_PMCU_MODULE_REGISTERED) { PMCU_ERR(PMCU,"Module %s is not registered; no power state can be returned.\n",ifx_pmcu_mod[pmcuModState->pmcuModule]); return IFX_PMCU_RETURN_ERROR; } /* check if the callback for getting module status is defined. */ if (ifx_pmcu_map[pmcuModState->pmcuModule].pmcuRegister[pmcuModState->pmcuModuleNr].ifx_pmcu_state_get == NULL) { PMCU_WARNING(PMCU, "Module %s has no valid Callback defined for ifx_pmcu_state_get\n",ifx_pmcu_mod[pmcuModState->pmcuModule]); return IFX_PMCU_RETURN_NOTDEFINED; } ret = ifx_pmcu_map[pmcuModState->pmcuModule].pmcuRegister[pmcuModState->pmcuModuleNr].ifx_pmcu_state_get(&pmcuState); if (ret != IFX_PMCU_RETURN_SUCCESS) { PMCU_ERR(PMCU, "Get state function return with error status for module %s\n",ifx_pmcu_mod[pmcuModState->pmcuModule]); return IFX_PMCU_RETURN_DENIED; } pmcuModState->pmcuState = pmcuState; return IFX_PMCU_RETURN_SUCCESS; } /** Recursive function to parse all dependency list's and create a accumulated list of modules and their necessary powerStates, for the requested powerState/module. The accumulated list is a static array called mcAccuDepList[]. \param[in] pmcuModuleState (module, subNr, newState) \return Returns value as follows: - IFX_PMCU_RETURN_SUCCESS: if successful - IFX_PMCU_RETURN_ERROR: in case of an error \ingroup IFX_PMCU_KERNEL */ static IFX_PMCU_RETURN_t ifx_pmcu_parse_deplist (IFX_PMCU_MODULE_STATE_t pmcuModuleState) { IFX_PMCU_MODULE_DEP_t *entryModDep = NULL; /* points to the Dependency struct of the requested subModule */ IFX_uint8_t i = 0; IFX_PMCU_MODULE_STATE_t moduleState; IFX_PMCU_MODULE_t pmcuModule = IFX_PMCU_MODULE_PMCU; IFX_PMCU_STATE_t pmcuState = IFX_PMCU_STATE_INVALID, pmcuOldState = IFX_PMCU_STATE_INVALID; IFX_uint8_t pmcuModuleNr = 0; PMCU_FUNC(PMCU, "%s is called for module=%s, moduleNr=%d,\n",__FUNCTION__,ifx_pmcu_mod[pmcuModuleState.pmcuModule],pmcuModuleState.pmcuModuleNr); //PMCU_DEBUG(PMCU, "parse start: pmcuState=%s, pmcuModule=%s\n",ifx_pmcu_ps[pmcuModuleState.pmcuState],ifx_pmcu_mod[pmcuModuleState.pmcuModule]); /* is module already processed? */ if (!ifx_pmcu_ModMem[pmcuModuleState.pmcuModule][pmcuModuleState.pmcuModuleNr]) { /* No. Module is not in the list. Create a new entry */ ifx_pmcu_ModMem[pmcuModuleState.pmcuModule][pmcuModuleState.pmcuModuleNr] = 1; /* set marker */ memcpy(&ifx_pmcu_accuDepList[ifx_pmcu_indexAccuDepL], &pmcuModuleState, sizeof(pmcuModuleState)); ifx_pmcu_indexAccuDepL++; } /* points to the Dependency struct of the requested subModule */ entryModDep = ifx_pmcu_map[pmcuModuleState.pmcuModule].pmcuRegister[pmcuModuleState.pmcuModuleNr].pmcuModuleDep; /* check if a dependency list exists for the module */ if (entryModDep == NULL) { /* end of processing; no further dependency list available. */ return IFX_PMCU_RETURN_SUCCESS; } /* Dependency list exist. Check if dependency list exceeds maximum number of entries 'IFX_DEPLIST_SIZE' */ if (entryModDep->nDepth >= IFX_DEPLIST_SIZE) { PMCU_ERR(PMCU, "Dependency list of module '%s' is greater than %i\n",ifx_pmcu_mod[pmcuModuleState.pmcuModule],IFX_DEPLIST_SIZE); return IFX_PMCU_RETURN_ERROR; } /* loop over the dependency list */ for (i = 0; i < entryModDep->nDepth; i++) { switch (pmcuModuleState.pmcuState) { /* switch on requested powerState */ case IFX_PMCU_STATE_D0: //PMCU_DEBUG(PMCU, "change to powerState IFX_PMCU_STATE_D0\n"); pmcuState = entryModDep->moduleStates[i].onState; pmcuModule = entryModDep->moduleStates[i].pmcuModule; pmcuModuleNr = entryModDep->moduleStates[i].pmcuModuleNr; break; case IFX_PMCU_STATE_D1: //PMCU_DEBUG(PMCU, "change to powerState IFX_PMCU_STATE_D1\n"); pmcuState = entryModDep->moduleStates[i].standBy; pmcuModule = entryModDep->moduleStates[i].pmcuModule; pmcuModuleNr = entryModDep->moduleStates[i].pmcuModuleNr; break; case IFX_PMCU_STATE_D2: //PMCU_DEBUG(PMCU, "change to powerState IFX_PMCU_STATE_D2\n"); pmcuState = entryModDep->moduleStates[i].lpStandBy; pmcuModule = entryModDep->moduleStates[i].pmcuModule; pmcuModuleNr = entryModDep->moduleStates[i].pmcuModuleNr; break; case IFX_PMCU_STATE_D3: //PMCU_DEBUG(PMCU, "change to powerState IFX_PMCU_STATE_D3\n"); pmcuState = entryModDep->moduleStates[i].offState; pmcuModule = entryModDep->moduleStates[i].pmcuModule; pmcuModuleNr = entryModDep->moduleStates[i].pmcuModuleNr; break; default: PMCU_ERR(PMCU, "invalid PowerState was selected\n"); /* error handling invalid state */ return IFX_PMCU_RETURN_ERROR; } /* get current state of the module from the dependency list */ pmcuOldState = ifx_pmcu_map[pmcuModule].pmcuOldState[pmcuModuleNr]; /* check if there is anything todo for this module */ /* compare the CURRENT state with the MASK from the dependency list */ //PMCU_DEBUG(PMCU, "oldState=%s, DepMask=%s, pmcuModule=%s\n",ifx_pmcu_ps[pmcuOldState],ifx_pmcu_ps[pmcuState],ifx_pmcu_mod[pmcuModule]); //PMCU_DEBUG_PRINT_MOD_MEM(); /* is module already processed? if yes skip this module */ if (ifx_pmcu_ModMem[pmcuModule][pmcuModuleNr]) { /* yes. Module is already in the list. goto next entry */ continue; } //PMCU_DEBUG(PMCU, "pmcu_stateMap=%d\n",ifx_pmcu_stateMap[pmcuState][pmcuOldState]); if (ifx_pmcu_stateMap[pmcuState][pmcuOldState] == 1) { /* here we have the recursive call of this function */ moduleState.pmcuModule = pmcuModule; moduleState.pmcuState = pmcuState; moduleState.pmcuModuleNr = pmcuModuleNr; ifx_pmcu_parse_deplist(moduleState); } else { /* fetch next module from depenedency list */ PMCU_DEBUG(PMCU, "no dependency activity necessary for module=%s, moduleNr=%d,\n",ifx_pmcu_mod[pmcuModule],pmcuModuleNr); } } return IFX_PMCU_RETURN_SUCCESS; } /** Apply all callbacks registered to be executed before a state change for pmcuModule. The input for this function is the accumulated dependency list ifx_pmcu_accuDepList[] which is filled by ifx_pmcu_parse_deplist(). \param[out] pmcuPreCallIndex index to the last element inside the ifx_pmcu_preCall[][] array \return Returns value as follows: - IFX_PMCU_RETURN_SUCCESS: if successful - IFX_PMCU_RETURN_ERROR: in case of an error - IFX_PMCU_RETURN_DENIED in case that the powerState change was rejected \ingroup IFX_PMCU_KERNEL */ static IFX_PMCU_RETURN_t ifx_pmcu_prechange (IFX_int8_t* pmcuPreCallIndex) { IFX_PMCU_RETURN_t ret = IFX_PMCU_RETURN_SUCCESS; IFX_int8_t i; IFX_PMCU_MODULE_t pmcuModule; IFX_PMCU_STATE_t pmcuState, pmcuOldState; IFX_uint8_t pmcuModuleNr; PMCU_FUNC(PMCU, "%s is called\n",__FUNCTION__); /* check if we have one or more entries in the table */ if (ifx_pmcu_indexAccuDepL == 0) { PMCU_DEBUG(PMCU, "nothing todo; accumulated depList is empty\n"); } /* loop over the accumulated dependency list in reverse order to follow the processing sequence defined in the dependency lists */ // for (i = 0; i < ifx_pmcu_indexAccuDepL; i++) { for (i = (ifx_pmcu_indexAccuDepL-1); i >= 0; i--) { pmcuModule = ifx_pmcu_accuDepList[i].pmcuModule; pmcuModuleNr = ifx_pmcu_accuDepList[i].pmcuModuleNr; pmcuState = ifx_pmcu_accuDepList[i].pmcuState; pmcuOldState = ifx_pmcu_map[pmcuModule].pmcuOldState[pmcuModuleNr]; /* check if module is registered in PMCU */ if (ifx_pmcu_map[pmcuModule].registryInfo[pmcuModuleNr] != IFX_PMCU_MODULE_REGISTERED) { PMCU_ERR(PMCU, "module=%s, moduleNr=%d, listed in dependency list is not registered in PMCU\n",ifx_pmcu_mod[pmcuModule],pmcuModuleNr); continue; } /* check if preCallback is defined */ if (ifx_pmcu_map[pmcuModule].pmcuRegister[pmcuModuleNr].pre == NULL) { PMCU_DEBUG(PMCU, "no preCallback defined for module=%s, moduleNr=%d\n",ifx_pmcu_mod[pmcuModule],pmcuModuleNr); continue; } /* mark pre call in list for later use in change/post callbacks */ (*pmcuPreCallIndex)++; /* next free entry */ ifx_pmcu_preCall[*pmcuPreCallIndex][0] = pmcuModule; /* module name */ ifx_pmcu_preCall[*pmcuPreCallIndex][1] = pmcuModuleNr; /* subModule no. */ ifx_pmcu_preCall[*pmcuPreCallIndex][2] = pmcuState; /* powerState */ ifx_pmcu_preCall[*pmcuPreCallIndex][3] = 1; /* preCall was made */ if (ifx_pmcu_map[pmcuModule].pmcuRegister[pmcuModuleNr].pre != NULL) { /* call pre callback of the module */ PMCU_DEBUG(PMCU, "pre Callback of module=%s, moduleNr=%d, is called\n",ifx_pmcu_mod[pmcuModule],pmcuModuleNr); ret = ifx_pmcu_map[pmcuModule].pmcuRegister[pmcuModuleNr].pre(pmcuModule, pmcuState, pmcuOldState); } if (ret == IFX_PMCU_RETURN_DENIED) { /* if one denied will be returned, pre-processing can be interrupted */ PMCU_WARNING(PMCU, "preCallback of module=%s, moduleNr=%d, return DENIED\n",ifx_pmcu_mod[pmcuModule],pmcuModuleNr); return IFX_PMCU_RETURN_DENIED; } if (ret == IFX_PMCU_RETURN_ERROR) { /* if one error will be returned, pre-processing can be interrupted */ PMCU_ERR(PMCU, "preCallback of module=%s, moduleNr=%d, return ERROR\n",ifx_pmcu_mod[pmcuModule],pmcuModuleNr); return IFX_PMCU_RETURN_DENIED; } if (ret == IFX_PMCU_RETURN_SUCCESS) { PMCU_DEBUG(PMCU, "module=%s, moduleNr=%d, accept preCallback\n",ifx_pmcu_mod[pmcuModule],pmcuModuleNr); } else { PMCU_DEBUG(PMCU, "module=%s, moduleNr=%d, NOACTIVITY in preCallback\n",ifx_pmcu_mod[pmcuModule],pmcuModuleNr); } } return IFX_PMCU_RETURN_SUCCESS; } /** Apply all callbacks registered to be executed if a state change is requested and accepted. \param[in] pmcuModuleState (module, subNr, newState) \param[out] pmcuPreCallIndex index to the last element inside the ifx_pmcu_preCall[][] array \return Returns value as follows: - IFX_PMCU_RETURN_SUCCESS: if successful - IFX_PMCU_RETURN_ERROR: in case of an error \ingroup IFX_PMCU_KERNEL */ static IFX_PMCU_RETURN_t ifx_pmcu_statechange (IFX_PMCU_MODULE_STATE_t pmcuModuleState, IFX_int8_t* pmcuPreCallIndex) { IFX_PMCU_RETURN_t ret = IFX_PMCU_RETURN_SUCCESS; IFX_uint8_t i; IFX_uint8_t pmcuModuleNr; IFX_PMCU_MODULE_t pmcuModule; IFX_PMCU_STATE_t pmcuState; PMCU_FUNC(PMCU, "%s is called\n",__FUNCTION__); /* check if there is anything todo */ if (*pmcuPreCallIndex == -1) { PMCU_DEBUG(PMCU, "nothing todo for statechange processing\n"); return IFX_PMCU_RETURN_SUCCESS; } /* loop over the ifx_pmcu_preCall[][] list to call the statechange callbacks */ for (i = 0; i <= *pmcuPreCallIndex; i++) { if (ifx_pmcu_preCall[i][3] != 1) { PMCU_ERR(PMCU, "error in ifx_pmcu_preCall[][] list during statechangeCallback processing\n"); return IFX_PMCU_RETURN_ERROR; } pmcuModule = ifx_pmcu_preCall[i][0]; /* fetch module name */ pmcuModuleNr = ifx_pmcu_preCall[i][1]; /* fetch subModule No. */ pmcuState = ifx_pmcu_preCall[i][2]; /* fetch powerState */ ifx_pmcu_preCall[i][4] = 1; /* statechangeCall was made */ if (ifx_pmcu_map[pmcuModule].pmcuRegister[pmcuModuleNr].ifx_pmcu_state_change != NULL) { PMCU_DEBUG(PMCU, "ifx_pmcu_state_change Callback of module=%s, moduleNr=%d, is called\n",ifx_pmcu_mod[pmcuModule],pmcuModuleNr); ret = ifx_pmcu_map[pmcuModule].pmcuRegister[pmcuModuleNr].ifx_pmcu_state_change(pmcuState); if ((ret == IFX_PMCU_RETURN_DENIED) || (ret == IFX_PMCU_RETURN_ERROR)) { PMCU_ERR(PMCU, "ifx_pmcu_state_change Callback of module=%s, moduleNr=%d, return DENIED or ERROR\n",ifx_pmcu_mod[pmcuModule],pmcuModuleNr); return IFX_PMCU_RETURN_ERROR; } } } return IFX_PMCU_RETURN_SUCCESS; } /** Apply all callbacks registered to be executed after a state change for pmcuModule was done, or a request was denied. \param[in] pmcuModuleState (module, subNr, newState) \param[out] pmcuPreCallIndex index to the last element inside the ifx_pmcu_preCall[][] array \return Returns value as follows: - IFX_PMCU_RETURN_SUCCESS: if successful - IFX_PMCU_RETURN_ERROR: in case of an error \ingroup IFX_PMCU_KERNEL */ static IFX_PMCU_RETURN_t ifx_pmcu_postchange (IFX_PMCU_MODULE_STATE_t pmcuModuleState, IFX_int8_t* pmcuPreCallIndex) { IFX_PMCU_RETURN_t ret = IFX_PMCU_RETURN_SUCCESS; IFX_uint8_t i,denied = 0; IFX_uint8_t pmcuModuleNr; IFX_PMCU_MODULE_t pmcuModule; IFX_PMCU_STATE_t pmcuState, pmcuOldState; PMCU_FUNC(PMCU, "%s is called\n",__FUNCTION__); /* check if there is anything todo */ if (*pmcuPreCallIndex == -1) { PMCU_DEBUG(PMCU, "nothing todo for statechange processing\n"); return IFX_PMCU_RETURN_SUCCESS; } /* check if stateChange was accepted or denied */ if (ifx_pmcu_map[pmcuModuleState.pmcuModule].pmcuOldState[pmcuModuleState.pmcuModuleNr] == pmcuModuleState.pmcuState) { PMCU_DEBUG(PMCU, "requested powerState was denied\n"); denied = 1; /* set denied indicator */ } /* loop over the ifx_pmcu_preCall[][] list to call the post callbacks */ for (i = 0; i <= *pmcuPreCallIndex; i++) { if (ifx_pmcu_preCall[i][3] != 1) { PMCU_ERR(PMCU, "error in ifx_pmcu_preCall[][] list during postCallback processing\n"); return IFX_PMCU_RETURN_ERROR; } pmcuModule = ifx_pmcu_preCall[i][0]; /* fetch module name */ pmcuModuleNr = ifx_pmcu_preCall[i][1]; /* fetch subModule No. */ pmcuState = ifx_pmcu_preCall[i][2]; /* fetch powerState */ ifx_pmcu_preCall[i][5] = 1; /* postCall was made */ pmcuOldState = ifx_pmcu_map[pmcuModule].pmcuOldState[pmcuModuleNr]; if (denied) { pmcuState = pmcuOldState; } if (ifx_pmcu_map[pmcuModule].pmcuRegister[pmcuModuleNr].post != NULL) { PMCU_DEBUG(PMCU, "post Callback of module=%s, moduleNr=%d, is called\n",ifx_pmcu_mod[pmcuModule],pmcuModuleNr); ret = ifx_pmcu_map[pmcuModule].pmcuRegister[pmcuModuleNr].post(pmcuModule, pmcuState, pmcuOldState); } if ((ret == IFX_PMCU_RETURN_DENIED) || (ret == IFX_PMCU_RETURN_ERROR)) { PMCU_ERR(PMCU, "post Callback of module=%s, moduleNr=%d, return DENIED or ERROR\n",ifx_pmcu_mod[pmcuModule],pmcuModuleNr); return IFX_PMCU_RETURN_ERROR; } if (ret == IFX_PMCU_RETURN_SUCCESS) { /* store new state inside PMCU mapTable */ ifx_pmcu_map[pmcuModule].pmcuOldState[pmcuModuleNr] = pmcuState; ifx_pmcu_map[pmcuModule].pmcuNewState[pmcuModuleNr] = pmcuState; } } return IFX_PMCU_RETURN_SUCCESS; } static ssize_t ifx_pmcu_read( struct file *filp, char *buf, size_t count, loff_t *ppos ) { return count; } static ssize_t ifx_pmcu_write( struct file *filp, const char *buf, size_t count, loff_t *ppos ) { return 0; } static int ifx_pmcu_open(struct inode * inode, struct file * file) { return 0; } static int ifx_pmcu_release(struct inode *inode, struct file *file) { return 0; } static unsigned int ifx_pmcu_poll(struct file *filp, poll_table *wait) { poll_wait(filp, &ifx_pmcu_wait_q, wait); return 0; } /** Configuration / Control for the device. \param inode Pointer to the inode. \param file Pointer to the file descriptor. \param cmd IOCTL identifier. \param arg Optional argument. \return - 0 and positive values - success - negative value - ioctl failed */ static int ifx_pmcu_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { int ret = IFX_ERROR; int retValue; IFX_PMCU_MODULE_STATE_t usrModuleState; IFX_int32_t llevel = 0; IFX_int32_t req_ctrl; IFX_PMCU_MODULE_PWR_FEATURE_STATE_t pwrFeatureState; switch (cmd) { case IFX_PMCU_STATE_GET: { PMCU_FUNC(PMCU, "%s IFX_PMCU_STATE_GET is called\n",__FUNCTION__); copy_from_user(&usrModuleState, (IFX_PMCU_MODULE_STATE_t *)arg, sizeof(IFX_PMCU_MODULE_STATE_t)); down(&ifx_pmcu_map_mutex); retValue = ifx_pmcu_get_state (&usrModuleState); up(&ifx_pmcu_map_mutex); if (retValue == IFX_PMCU_RETURN_SUCCESS) { ret = IFX_SUCCESS; } copy_to_user((IFX_PMCU_MODULE_STATE_t *)arg, &usrModuleState, sizeof(IFX_PMCU_MODULE_STATE_t)); ret = IFX_SUCCESS; break; } case IFX_PMCU_STATE_REQ: { PMCU_FUNC(PMCU, "%s IFX_PMCU_STATE_REQ is called\n",__FUNCTION__); copy_from_user(&usrModuleState, (IFX_PMCU_MODULE_STATE_t *)arg, sizeof(IFX_PMCU_MODULE_STATE_t)); retValue = ifx_pmcu_state_req (usrModuleState.pmcuModule, usrModuleState.pmcuModuleNr, usrModuleState.pmcuState); if (retValue == IFX_PMCU_RETURN_SUCCESS) { ret = IFX_SUCCESS; } break; } case IFX_PMCU_REQ_CTRL: { PMCU_FUNC(PMCU, "%s IFX_PMCU_REQ_CTRL is called\n",__FUNCTION__); copy_from_user(&req_ctrl, (IFX_int32_t *)arg, sizeof(IFX_int32_t)); if (req_ctrl == 0) { request_control_g = 0; } else { request_control_g = 1; } ret = IFX_SUCCESS; break; } case IFX_PMCU_LOG_LEVEL: { PMCU_FUNC(PMCU, "%s IFX_PMCU_LOG_LEVEL is called\n",__FUNCTION__); copy_from_user(&llevel, (IFX_int32_t *)arg, sizeof(IFX_int32_t)); log_level_g = llevel; ret = IFX_SUCCESS; break; } case IFX_PMCU_PWR_FEATURE_SWITCH: { PMCU_FUNC(PMCU, "%s IFX_PMCU_PWR_FEATURE_SWITCH is called\n",__FUNCTION__); copy_from_user(&pwrFeatureState, (IFX_PMCU_MODULE_PWR_FEATURE_STATE_t *)arg, sizeof(IFX_PMCU_MODULE_PWR_FEATURE_STATE_t)); down(&ifx_pmcu_map_mutex); retValue = ifx_pmcu_pwr_feature_switch (pwrFeatureState); up(&ifx_pmcu_map_mutex); if (retValue == IFX_PMCU_RETURN_SUCCESS) { ret = IFX_SUCCESS; } break; } default: ret = IFX_ERROR; } return ret; } static struct file_operations ifx_pmcu_fops = { #ifdef MODULE owner: THIS_MODULE, #endif /* MODULE */ read: ifx_pmcu_read, write: ifx_pmcu_write, ioctl: ifx_pmcu_ioctl, open: ifx_pmcu_open, release: ifx_pmcu_release, poll: ifx_pmcu_poll, }; #ifdef CONFIG_PROC_FS static int ifx_pmcu_proc_device_list(char *page, char **start, off_t offset, int count, int *eof, void *data) { int len = 0; char *buf = page+offset; int i = 0, j = 0; IFX_uint32_t flags = 0; IFX_PMCU_MODULE_STATE_t pmcuModState; // PMCU_INFO(PMCU_1,"buf=%p, offset=%lu, count=%d, datap=%p, eof=%p, len=%d\n",buf,(unsigned long)offset,count,data,eof,len); //PMCU_FUNC(PMCU, "%s is called\n",__FUNCTION__); if(offset > 0) {/*the read function is called from the kernel until it returns 0.*/ return 0; } len += sprintf(buf+len, "PMCU log_level = %d\n",log_level_g); len += sprintf(buf+len, "PMCU Request Control = %s\n\n",((request_control_g == 1)?"Enabled":"Disabled")); len += sprintf(buf+len, "Legend:\n"); len += sprintf(buf+len, "\tr\t:registered (-=not registered, 1=unregistered, 2=registered)\n"); len += sprintf(buf+len, "\tSub#\t:Submodule or module instance\n"); len += sprintf(buf+len, "\tps\t:power state (-1=invalid state, D0=on, D1=lp1, D2=lp2, D3=off n.d.=no pmcu_state_get() defined)\n"); len += sprintf(buf+len, "\tf\t:power-saving-feature disabled (0=enabled, 1=disabled)(bit0=sub0,bit1=sub1,...,bit5=sub5)\n\n"); len += sprintf(buf+len, "Modules\t\tSub0\tSub1\tSub2\tSub3\tSub4\tSub5\n"); len += sprintf(buf+len, "\t\tr/ps/f\tr/ps/f\tr/ps/f\tr/ps/f\tr/ps/f\tr/ps/f\n"); down(&ifx_pmcu_map_mutex); for (i = 0; i < IFX_PMCU_MODULE_ID_MAX ; i++) { if (strlen(ifx_pmcu_map[i].pmcuModuleName) < 8 ) { len += sprintf(buf + len, "%s\t\t", ifx_pmcu_map[i].pmcuModuleName); } else { len += sprintf(buf + len, "%s\t", ifx_pmcu_map[i].pmcuModuleName); } for (j = 0; j < MAX_PMCU_SUB_MODULE; j++) { if ( (ifx_pmcu_map[i].registryInfo[j] != IFX_PMCU_MODULE_REGISTERED) || (ifx_pmcu_map[i].pmcuRegister[j].pmcuModule == IFX_PMCU_MODULE_PMCU) ) { len += sprintf(buf+len,"-\t"); } else { len += sprintf(buf + len, "%d/", ifx_pmcu_map[i].registryInfo[j]); pmcuModState.pmcuModule = ifx_pmcu_map[i].pmcuRegister[j].pmcuModule; pmcuModState.pmcuModuleNr = j; if(IFX_PMCU_RETURN_SUCCESS == ifx_pmcu_get_state(&pmcuModState)){ len += sprintf(buf+len,"%s/",ifx_pmcu_ps[pmcuModState.pmcuState]); }else{ len += sprintf(buf+len,"%s/",ifx_pmcu_ps[ifx_pmcu_map[i].pmcuOldState[j]]); } flags = (ifx_pmcu_map[i].dbgFlag) & (1<>j); } else { len += sprintf(buf+len,"%d\t",flags>>j); } } } len += sprintf(buf+len,"\n"); } up(&ifx_pmcu_map_mutex); // PMCU_INFO(PMCU_2,"buf=%p, offset=%lu, count=%d, datap=%p, eof=%p, len=%d\n",buf,(unsigned long)offset,count,data,eof,len); return len; } static int ifx_pmcu_proc_version(char *buf, char **start, off_t offset, int count, int *eof, void *data) { int len = 0; PMCU_FUNC(PMCU, "%s is called\n",__FUNCTION__); len += sprintf(buf+len, "IFX_PMCU Version = %s\n",IFX_PMCU_DRV_VERSION); len += sprintf(buf+len, "Compiled on %s, %s for Linux kernel %s\n",__DATE__, __TIME__, UTS_RELEASE); return len; } static int ifx_pmcu_proc_get_internal_map(char *buf, char **start, off_t offset, int count, int *eof, void *data) { int len = 0; int i=0, j = 0; PMCU_FUNC(PMCU, "%s is called\n",__FUNCTION__); len += sprintf(buf+len, "internal MAP-Table\n"); // PMCU_FUNC(PMCU_1,"buf=%p, offset=%lu, count=%d, datap=%p, eof=%p, len=%d\n",buf,(unsigned long)offset,count,data,eof,len); down(&ifx_pmcu_map_mutex); len += sprintf(buf + len, "pmcu_map[%d]->ModuleName=%s\n", (int)data, ifx_pmcu_map[(int)data].pmcuModuleName); for (j = 0; j < MAX_PMCU_SUB_MODULE; j++) { len += sprintf(buf + len, "regInfo [%d]=%d\t", j, ifx_pmcu_map[(int)data].registryInfo[j]); } len += sprintf(buf+len,"\n"); for (j = 0; j < MAX_PMCU_SUB_MODULE; j++) { len += sprintf(buf + len, "OldState[%d]=%d\t", j, ifx_pmcu_map[(int)data].pmcuOldState[j]); } len += sprintf(buf+len,"\n"); for (j = 0; j < MAX_PMCU_SUB_MODULE; j++) { len += sprintf(buf + len, "NewState[%d]=%d\t", j, ifx_pmcu_map[(int)data].pmcuNewState[j]); } len += sprintf(buf+len,"\n\n"); for (j = 0; j < MAX_PMCU_SUB_MODULE; j++) { len += sprintf(buf + len, "Reg[%d]->Module=%d\n", j, ifx_pmcu_map[(int)data].pmcuRegister[j].pmcuModule); len += sprintf(buf + len, "Reg[%d]->ModuleNr=%d\n", j, ifx_pmcu_map[(int)data].pmcuRegister[j].pmcuModuleNr); len += sprintf(buf + len, "Reg[%d]->ifx_pmcu_state_change=%p\n", j, ifx_pmcu_map[(int)data].pmcuRegister[j].ifx_pmcu_state_change); len += sprintf(buf + len, "Reg[%d]->ifx_pmcu_state_get=%p\n", j, ifx_pmcu_map[(int)data].pmcuRegister[j].ifx_pmcu_state_get); len += sprintf(buf + len, "Reg[%d]->ifx_pmcu_pwr_feature_switch=%p\n", j, ifx_pmcu_map[(int)data].pmcuRegister[j].ifx_pmcu_pwr_feature_switch); len += sprintf(buf + len, "Reg[%d]->pre=%p\n", j, ifx_pmcu_map[(int)data].pmcuRegister[j].pre); len += sprintf(buf + len, "Reg[%d]->post=%p\n", j, ifx_pmcu_map[(int)data].pmcuRegister[j].post); len += sprintf(buf + len, "Reg[%d]->ModuleDep=%p\n", j, ifx_pmcu_map[(int)data].pmcuRegister[j].pmcuModuleDep); if (ifx_pmcu_map[(int)data].pmcuRegister[j].pmcuModuleDep != NULL) { len += sprintf(buf + len, "Reg[%d]->ModuleDep->nDepth=%d\n", j, ifx_pmcu_map[(int)data].pmcuRegister[j].pmcuModuleDep->nDepth); } len += sprintf(buf+len,"\n"); } for (j = 0; j < MAX_PMCU_SUB_MODULE; j++) { if (ifx_pmcu_map[(int)data].pmcuRegister[j].pmcuModuleDep != NULL) { for (i=0;inDepth;i++) { len += sprintf(buf + len, "Reg[%d]->Dep[%d]->Module =%s\n", j, i, ifx_pmcu_mod[ifx_pmcu_map[(int)data].pmcuRegister[j].pmcuModuleDep->moduleStates[i].pmcuModule]); len += sprintf(buf + len, "Reg[%d]->Dep[%d]->ModuleNr =%d\n", j, i, ifx_pmcu_map[(int)data].pmcuRegister[j].pmcuModuleDep->moduleStates[i].pmcuModuleNr); len += sprintf(buf + len, "Reg[%d]->Dep[%d]->onState =%d\n", j, i, ifx_pmcu_map[(int)data].pmcuRegister[j].pmcuModuleDep->moduleStates[i].onState); len += sprintf(buf + len, "Reg[%d]->Dep[%d]->standBy =%d\n", j, i, ifx_pmcu_map[(int)data].pmcuRegister[j].pmcuModuleDep->moduleStates[i].standBy); len += sprintf(buf + len, "Reg[%d]->Dep[%d]->lpStandBy=%d\n", j, i, ifx_pmcu_map[(int)data].pmcuRegister[j].pmcuModuleDep->moduleStates[i].lpStandBy); len += sprintf(buf + len, "Reg[%d]->Dep[%d]->offState =%d\n", j, i, ifx_pmcu_map[(int)data].pmcuRegister[j].pmcuModuleDep->moduleStates[i].offState); len += sprintf(buf+len,"\n"); } } } up(&ifx_pmcu_map_mutex); // PMCU_FUNC(PMCU_2,"buf=%p, offset=%lu, count=%d, datap=%p, eof=%p, len=%d\n",buf,(unsigned long)offset,count,data,eof,len); len += sprintf(buf+len,"\n"); return len; } /** Initialize and install the proc entry \return -1 or 0 on success */ static int ifx_pmcu_proc_EntriesInstall(void) { PMCU_FUNC(PMCU, "%s is called\n",__FUNCTION__); /*create pmcu proc entry*/ g_ifx_pmcu_dir_pmcu = proc_mkdir("pmcu", NULL); create_proc_read_entry("device_list", 0, g_ifx_pmcu_dir_pmcu, ifx_pmcu_proc_device_list, NULL); create_proc_read_entry("version", 0, g_ifx_pmcu_dir_pmcu, ifx_pmcu_proc_version, NULL); g_ifx_pmcu_dir_debug = proc_mkdir("pmcu/debug", NULL); create_proc_read_entry("cpu", 0, g_ifx_pmcu_dir_debug, ifx_pmcu_proc_get_internal_map, (void*)IFX_PMCU_MODULE_CPU); create_proc_read_entry("eth", 0, g_ifx_pmcu_dir_debug, ifx_pmcu_proc_get_internal_map, (void*)IFX_PMCU_MODULE_ETH); create_proc_read_entry("usb", 0, g_ifx_pmcu_dir_debug, ifx_pmcu_proc_get_internal_map, (void*)IFX_PMCU_MODULE_USB); create_proc_read_entry("dsl", 0, g_ifx_pmcu_dir_debug, ifx_pmcu_proc_get_internal_map, (void*)IFX_PMCU_MODULE_DSL); create_proc_read_entry("wlan", 0, g_ifx_pmcu_dir_debug, ifx_pmcu_proc_get_internal_map, (void*)IFX_PMCU_MODULE_WLAN); create_proc_read_entry("dect", 0, g_ifx_pmcu_dir_debug, ifx_pmcu_proc_get_internal_map, (void*)IFX_PMCU_MODULE_DECT); create_proc_read_entry("fxs", 0, g_ifx_pmcu_dir_debug, ifx_pmcu_proc_get_internal_map, (void*)IFX_PMCU_MODULE_FXS); create_proc_read_entry("fxo", 0, g_ifx_pmcu_dir_debug, ifx_pmcu_proc_get_internal_map, (void*)IFX_PMCU_MODULE_FXO); create_proc_read_entry("ve", 0, g_ifx_pmcu_dir_debug, ifx_pmcu_proc_get_internal_map, (void*)IFX_PMCU_MODULE_VE); create_proc_read_entry("ppe", 0, g_ifx_pmcu_dir_debug, ifx_pmcu_proc_get_internal_map, (void*)IFX_PMCU_MODULE_PPE); create_proc_read_entry("switch", 0, g_ifx_pmcu_dir_debug, ifx_pmcu_proc_get_internal_map, (void*)IFX_PMCU_MODULE_SWITCH); create_proc_read_entry("uart", 0, g_ifx_pmcu_dir_debug, ifx_pmcu_proc_get_internal_map, (void*)IFX_PMCU_MODULE_UART); create_proc_read_entry("spi", 0, g_ifx_pmcu_dir_debug, ifx_pmcu_proc_get_internal_map, (void*)IFX_PMCU_MODULE_SPI); create_proc_read_entry("sdio", 0, g_ifx_pmcu_dir_debug, ifx_pmcu_proc_get_internal_map, (void*)IFX_PMCU_MODULE_SDIO); create_proc_read_entry("pci", 0, g_ifx_pmcu_dir_debug, ifx_pmcu_proc_get_internal_map, (void*)IFX_PMCU_MODULE_PCI); create_proc_read_entry("vlynq", 0, g_ifx_pmcu_dir_debug, ifx_pmcu_proc_get_internal_map, (void*)IFX_PMCU_MODULE_VLYNQ); create_proc_read_entry("deu", 0, g_ifx_pmcu_dir_debug, ifx_pmcu_proc_get_internal_map, (void*)IFX_PMCU_MODULE_DEU); create_proc_read_entry("cpu_ps", 0, g_ifx_pmcu_dir_debug, ifx_pmcu_proc_get_internal_map, (void*)IFX_PMCU_MODULE_CPU_PS); create_proc_read_entry("gptu", 0, g_ifx_pmcu_dir_debug, ifx_pmcu_proc_get_internal_map, (void*)IFX_PMCU_MODULE_GPTC); create_proc_read_entry("usif_uart", 0, g_ifx_pmcu_dir_debug, ifx_pmcu_proc_get_internal_map, (void*)IFX_PMCU_MODULE_USIF_UART); create_proc_read_entry("usif_spi", 0, g_ifx_pmcu_dir_debug, ifx_pmcu_proc_get_internal_map, (void*)IFX_PMCU_MODULE_USIF_SPI); create_proc_read_entry("pcie", 0, g_ifx_pmcu_dir_debug, ifx_pmcu_proc_get_internal_map, (void*)IFX_PMCU_MODULE_PCIE); return 0; } /** Initialize and install the proc entry \return -1 or 0 on success */ static int ifx_pmcu_proc_EntriesRemove(void) { PMCU_FUNC(PMCU, "%s is called\n",__FUNCTION__); remove_proc_entry("pcie", g_ifx_pmcu_dir_debug); remove_proc_entry("usif_spi", g_ifx_pmcu_dir_debug); remove_proc_entry("usif_uart", g_ifx_pmcu_dir_debug); remove_proc_entry("gptu", g_ifx_pmcu_dir_debug); remove_proc_entry("cpu_ps", g_ifx_pmcu_dir_debug); remove_proc_entry("deu", g_ifx_pmcu_dir_debug); remove_proc_entry("vlynq", g_ifx_pmcu_dir_debug); remove_proc_entry("pci", g_ifx_pmcu_dir_debug); remove_proc_entry("sdio", g_ifx_pmcu_dir_debug); remove_proc_entry("spi", g_ifx_pmcu_dir_debug); remove_proc_entry("uart", g_ifx_pmcu_dir_debug); remove_proc_entry("switch", g_ifx_pmcu_dir_debug); remove_proc_entry("ppe", g_ifx_pmcu_dir_debug); remove_proc_entry("ve", g_ifx_pmcu_dir_debug); remove_proc_entry("fxo", g_ifx_pmcu_dir_debug); remove_proc_entry("fxs", g_ifx_pmcu_dir_debug); remove_proc_entry("dect", g_ifx_pmcu_dir_debug); remove_proc_entry("wlan", g_ifx_pmcu_dir_debug); remove_proc_entry("dsl", g_ifx_pmcu_dir_debug); remove_proc_entry("usb", g_ifx_pmcu_dir_debug); remove_proc_entry("eth", g_ifx_pmcu_dir_debug); remove_proc_entry("cpu", g_ifx_pmcu_dir_debug); remove_proc_entry(g_ifx_pmcu_dir_debug->name, NULL); g_ifx_pmcu_dir_debug = NULL; remove_proc_entry("version", g_ifx_pmcu_dir_pmcu); remove_proc_entry("device_list", g_ifx_pmcu_dir_pmcu); remove_proc_entry(g_ifx_pmcu_dir_pmcu->name, NULL); g_ifx_pmcu_dir_pmcu = NULL; return 0; } #endif /* CONFIG_PROC_FS */ static int __init ifx_pmcu_init(void) { int err = 0; int i = 0; IFX_ulong_t iflags; PMCU_FUNC(PMCU, "%s is called\n",__FUNCTION__); /* PMCU device map table is initialized during declaration */ /* init powerState request ringbuffer */ memset(ifx_pmcu_reqBuffer, 0, sizeof(ifx_pmcu_reqBuffer)); /* register port device */ #ifdef PMCU_DYNAMIC_MAJOR_NO ifx_pmcu_major = register_chrdev (0, IFX_PMCU_DEVICE_NAME, &ifx_pmcu_fops); if (ifx_pmcu_major < 0) { PMCU_ERR(PMCU, "ifx-pmcu: Error! Could not register pmcu device. #%d\n", err); return ifx_pmcu_major; } #else err = register_chrdev (ifx_pmcu_major, IFX_PMCU_DEVICE_NAME, &ifx_pmcu_fops); if (err != 0) { PMCU_ERR(PMCU, "ifx-pmcu: Error! Could not register pmcu device. #%d\n", err); return err; } #endif #ifdef CONFIG_PROC_FS ifx_pmcu_proc_EntriesInstall(); #endif down(&ifx_pmcu_map_mutex); spin_lock_irqsave(&ifx_pmcu_lock, iflags); /* Init PMCU device map table: only module name; prerequisite->compiler initialise maptable to NULL!! Sequence of calling module_init by the kernel can not be influenced in a regulare way. Therefore it may happen that a pmcu_register is called by an other module before pmcu_module_init is called. That's why the map table must be initialised before runtime! */ for (i = 0; i < IFX_PMCU_MODULE_ID_MAX; i++) { ifx_pmcu_map[i].pmcuModuleName = ifx_pmcu_mod[i]; } spin_unlock_irqrestore(&ifx_pmcu_lock, iflags); up(&ifx_pmcu_map_mutex); init_waitqueue_head(&ifx_pmcu_wait_q); PMCU_DEBUG(PMCU, "Lantiq PMCU Device Driver Version %s\n", IFX_PMCU_DRV_VERSION); return 0; } static void __exit ifx_pmcu_exit (void) { int i = 0, j = 0; IFX_ulong_t iflags; PMCU_FUNC(PMCU, "%s is called\n",__FUNCTION__); down(&ifx_pmcu_map_mutex); spin_lock_irqsave(&ifx_pmcu_lock, iflags); /* clear PMCU device map table: first free allocated memory for dependency list's; than set entire map table to '0'*/ for (i = 0; i < IFX_PMCU_MODULE_ID_MAX; i++) { for (j = 0; j < MAX_PMCU_SUB_MODULE; j++) { if (ifx_pmcu_map[i].pmcuRegister[j].pmcuModuleDep != NULL) { kfree(ifx_pmcu_map[i].pmcuRegister[j].pmcuModuleDep); } } } memset(ifx_pmcu_map, 0, sizeof(IFX_PMCU_MAP_t)); spin_unlock_irqrestore(&ifx_pmcu_lock, iflags); up(&ifx_pmcu_map_mutex); #ifdef CONFIG_PROC_FS ifx_pmcu_proc_EntriesRemove(); #endif unregister_chrdev(ifx_pmcu_major, IFX_PMCU_DEVICE_NAME); return; } module_init (ifx_pmcu_init); module_exit (ifx_pmcu_exit); EXPORT_SYMBOL(ifx_pmcu_state_req); EXPORT_SYMBOL(ifx_pmcu_register); EXPORT_SYMBOL(ifx_pmcu_unregister); MODULE_LICENSE ("GPL"); MODULE_AUTHOR("Lantiq Deutschland GmbH"); MODULE_DESCRIPTION ("LANTIQ PMCU driver"); MODULE_SUPPORTED_DEVICE ("Amazon SE, Danube, XRX100, XRX200");