| /** \file vp880_lp_control.c |
| * vp880_lp_control.c |
| * |
| * This file contains the control functions for the Vp880 device API for LPM |
| * termination types. |
| * |
| * Copyright (c) 2011, Microsemi |
| * |
| * $Revision: 1.1.2.1.8.3 $ |
| * $LastChangedDate: 2010-03-16 09:44:15 -0500 (Tue, 16 Mar 2010) $ |
| */ |
| |
| #include "../includes/vp_api_cfg.h" |
| |
| #if defined (VP_CC_880_SERIES) && defined (VP880_LP_SUPPORT) |
| |
| /* INCLUDES */ |
| #include "../../arch/uvb/vp_api_types.h" |
| #include "../../arch/uvb/vp_hal.h" |
| #include "../includes/vp_api_int.h" |
| #include "../includes/vp880_api.h" |
| #include "../vp880_api/vp880_api_int.h" |
| #include "../../arch/uvb/sys_service.h" |
| |
| /**< Functions called by Set Line State to abstract device states used for ABS |
| * and non ABS devices (if they are different). Set line state then operates |
| * on device state only (abstracted from API-II line state). |
| */ |
| static void |
| Vp880WriteLPEnterRegisters( |
| VpLineCtxType *pLineOtx, |
| VpDeviceIdType deviceId, |
| uint8 ecVal, |
| uint8 *lineState); |
| |
| /** |
| * Vp880SetDiscTimers() |
| * This function provides the value for Disconnect Timing using LPM. |
| * |
| * Preconditions: |
| * None. |
| * |
| * Postconditions: |
| * None. Returns a value in ms. |
| */ |
| uint16 |
| Vp880SetDiscTimers( |
| Vp880DeviceObjectType *pDevObj) |
| { |
| /* |
| * Only two options -- Fixed Ringing (long discharge), or Tracked Ringing |
| * (short discharge). |
| */ |
| /* |
| * This is actually checking CH0 (SWY) Switching Regulator mode which |
| * technically could be different from CH1 (SWZ). But since Profile Wizard |
| * doesn't support different CH0/CH1 Ringing Mode configurations AND it's |
| * not practical in a real application, we can get away with checking just |
| * one of the settings. Best to use CH0 in case a single channel device |
| * defines the second setting as RSVD and set to a single value in all |
| * cases. |
| */ |
| if (pDevObj->swParams[VP880_REGULATOR_TRACK_INDEX] |
| & VP880_REGULATOR_FIXED_RING_SWY) { |
| /* |
| * Longest is using "fixed" ringing mode because the external |
| * capacitors are generally very large. |
| */ |
| return VP880_FIXED_TRACK_DISABLE_TIME; |
| } else { |
| return VP880_INVERT_BOOST_DISABLE_TIME; |
| } |
| } |
| |
| /** |
| * Vp880RunLPDisc() |
| * This function implements the Disconnect Enter/Exit for Low Power Mode. |
| * |
| * Preconditions: |
| * None. Calling function must know that this code should execute. |
| * |
| * Postconditions: |
| * Initial procedures are performed and timers set to enter or exit Disconnect |
| * state for Low Power termination type. |
| */ |
| void |
| Vp880RunLPDisc( |
| VpLineCtxType *pLineCtx, |
| bool discMode, |
| uint8 nextSlicByte) |
| { |
| Vp880LineObjectType *pLineObj = pLineCtx->pLineObj; |
| uint8 channelId = pLineObj->channelId; |
| uint8 ecVal = pLineObj->ecVal; |
| |
| VpDevCtxType *pDevCtx = pLineCtx->pDevCtx; |
| Vp880DeviceObjectType *pDevObj = pDevCtx->pDevObj; |
| VpDeviceIdType deviceId = pDevObj->deviceId; |
| |
| uint8 mpiBuffer[7 + VP880_ICR2_LEN + VP880_ICR1_LEN + VP880_DEV_MODE_LEN |
| + VP880_SYS_STATE_LEN + VP880_ICR5_LEN + VP880_ICR3_LEN |
| + VP880_ICR4_LEN]; |
| uint8 mpiIndex = 0; |
| |
| /* devState "set" means the other line is in a LPM state. */ |
| Vp880DeviceStateIntType devState = (channelId == 0) ? |
| (pDevObj->stateInt & VP880_LINE1_LP) : |
| (pDevObj->stateInt & VP880_LINE0_LP); |
| |
| /* |
| * myDevState "set" means *this* line is in a LPM state prior to calling |
| * this function. |
| */ |
| Vp880DeviceStateIntType myDevState = (channelId == 0) ? |
| (pDevObj->stateInt & VP880_LINE0_LP) : |
| (pDevObj->stateInt & VP880_LINE1_LP); |
| |
| /* |
| * Enter/Exit Disconnect uses Active (w/Polarity) to cause fast charge or |
| * discharge of the line. |
| */ |
| uint8 lineState[] = {VP880_SS_ACTIVE}; |
| |
| bool lineInTest = Vp880IsChnlUndrTst(pDevObj, channelId); |
| uint16 leakyLine = pLineObj->status & VP880_LINE_LEAK; |
| |
| bool hookStatus; |
| |
| /* |
| * If coming from a Feed state to Disconnect, OK to check on hook state to |
| * determine if other line was in LPM. But don't run this test if coming |
| * from Disconnect because the hook state is frozen in Disconnect but |
| * other line could have been set to LPM. In that case, try to enter LPM |
| * and if exists a real off-hook, the VP-API-II will resolve it in hook |
| * management. |
| */ |
| if (discMode == TRUE) { |
| VpCSLACGetLineStatus(pLineCtx, VP_INPUT_RAW_HOOK, &hookStatus); |
| leakyLine = (hookStatus == FALSE) ? leakyLine : VP880_LINE_LEAK; |
| } |
| |
| VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("Vp880RunLPDisc+")); |
| |
| /* |
| * The other line is not really in LPM if this line is in test or has a |
| * leaky line condition. |
| */ |
| devState = (lineInTest == TRUE) ? 0 : devState; |
| devState = ((leakyLine == VP880_LINE_LEAK) ? 0 : devState); |
| |
| if (discMode == TRUE) { /* Entering Disconnect */ |
| VP_LINE_STATE(VpLineCtxType, pLineCtx, ("Entering Disconnect on Chan %d time %d", |
| channelId, pDevObj->timeStamp)); |
| pDevObj->stateInt |= ((channelId == 0) ? VP880_LINE0_LP : VP880_LINE1_LP); |
| |
| /* |
| * There are three cases to consider when entering Disconnect: |
| * 1. This line is coming from LPM-Standby and other line is in LPM. |
| * 2. This line is coming from a non-LPM state and other line is in LPM. |
| * 3. This line is coming from any state and other line is not in LPM. |
| * Condition #3 occurs also if this line has a resistive leak. |
| * |
| * All cases require the ICR values modified to disable the switcher. |
| */ |
| |
| /* |
| * Step 1: Program all ICR registers including Disable Switcher. Note |
| * these are writing to cached values only, not to the device -- yet. |
| */ |
| |
| Vp880SetLPRegisters(pLineObj, TRUE); |
| pLineObj->icr2Values[VP880_ICR2_SENSE_INDEX] &= ~VP880_ICR2_ILA_DAC; |
| pLineObj->icr2Values[VP880_ICR2_SENSE_INDEX] |= VP880_ICR2_VOC_DAC_SENSE; |
| pLineObj->icr2Values[VP880_ICR2_SENSE_INDEX+1] &= ~VP880_ICR2_VOC_DAC_SENSE; |
| |
| pLineObj->icr2Values[VP880_ICR2_SWY_CTRL_INDEX] |= VP880_ICR2_SWY_CTRL_EN; |
| pLineObj->icr2Values[VP880_ICR2_SWY_CTRL_INDEX+1] &= ~VP880_ICR2_SWY_CTRL_EN; |
| |
| /* |
| * Always start by disabling the switcher. Remaining steps will be done |
| * either in this function (immediate) or delayed. In all cases, the |
| * final state of Disconnect is delayed in order to allow the supply |
| * to discharge once disabled. |
| */ |
| VP_LINE_STATE(VpLineCtxType, pLineCtx,
|
| ("LP: Entering Disconnect: Channel %d: Writing ICR2 0x%02X 0x%02X 0x%02X 0x%02X time %d", |
| pLineObj->channelId, |
| pLineObj->icr2Values[0], pLineObj->icr2Values[1], |
| pLineObj->icr2Values[2], pLineObj->icr2Values[3], pDevObj->timeStamp)); |
| |
| mpiIndex = VpCSLACBuildMpiBuffer(mpiIndex, mpiBuffer, VP880_ICR2_WRT, |
| VP880_ICR2_LEN, pLineObj->icr2Values); |
| |
| /* |
| * Force Tip, Ring and Line Bias Override and set values to max. This |
| * forces values toward ground. |
| */ |
| pLineObj->icr1Values[VP880_ICR1_BIAS_OVERRIDE_LOCATION] |= |
| (VP880_ICR1_TIP_BIAS_OVERRIDE | VP880_ICR1_LINE_BIAS_OVERRIDE); |
| pLineObj->icr1Values[VP880_ICR1_BIAS_OVERRIDE_LOCATION+1] |= |
| (VP880_ICR1_TIP_BIAS_OVERRIDE | VP880_ICR1_LINE_BIAS_OVERRIDE); |
| |
| pLineObj->icr1Values[VP880_ICR1_RING_BIAS_OVERRIDE_LOCATION] |= |
| VP880_ICR1_RING_BIAS_OVERRIDE; |
| pLineObj->icr1Values[VP880_ICR1_RING_BIAS_OVERRIDE_LOCATION+1] |= |
| VP880_ICR1_RING_BIAS_OVERRIDE; |
| |
| mpiIndex = Vp880ProtectedWriteICR1(pLineObj, mpiIndex, mpiBuffer); |
| |
| if ((devState) && (myDevState)) { |
| /* |
| * 1. This line is coming from LPM-Standby and other line is in LPM. |
| * Step 1: Program all ICR registers including Disable Switcher. - done |
| * Step 2: Set line to Active (for fast discharge) |
| * Step 3: Delay, then set line to VP_LINE_DISCONNECT. - outside |
| * this if statement. |
| */ |
| VP_LINE_STATE(VpLineCtxType, pLineCtx,
|
| ("Entering Disconnect: Channel %d: Case 1 State 0x%02X time %d", |
| pLineObj->channelId, lineState[0], pDevObj->timeStamp)); |
| |
| /* |
| * Step 2: Line is in LPM-Standby (i.e., Disconnect) so need to |
| * set to Active to cause fast discharge |
| */ |
| if (pDevObj->staticInfo.rcnPcn[VP880_RCN_LOCATION] > VP880_REV_VC) { |
| Vp880UpdateBufferChanSel(pDevObj, channelId, lineState[0], FALSE); |
| mpiIndex = VpCSLACBuildMpiBuffer(mpiIndex, mpiBuffer, VP880_DEV_MODE_WRT, |
| VP880_DEV_MODE_LEN, pDevObj->devMode); |
| } |
| |
| if (pLineObj->slicValueCache != lineState[0]) { |
| pLineObj->slicValueCache = lineState[0]; |
| mpiIndex = VpCSLACBuildMpiBuffer(mpiIndex, mpiBuffer, VP880_SYS_STATE_WRT, |
| VP880_SYS_STATE_LEN, &pLineObj->slicValueCache); |
| } |
| VpMpiCmdWrapper(deviceId, pLineObj->ecVal, mpiBuffer[0], mpiIndex-1, |
| &mpiBuffer[1]); |
| mpiIndex = 0; |
| } else if ((devState) && (!myDevState)) { |
| /* |
| * 2. This line is coming from a non-LPM state and other line is in LPM. |
| * Step 1: Program all ICR registers including Disable Switcher. - done |
| * Step 2: Set line to Active w/Polarity (for fast discharge) |
| * Step 3: Delay, then set device to LPM - done at next tick. |
| */ |
| |
| /* |
| * Step 2: Set line to Active w/Polarity (for fast discharge). Note |
| * that the state may already be in a condition that will allow fast |
| * discharge, but it could also be in Tip Open or Ringing or "other" |
| * so we just want to be sure it's in a state that we know. |
| */ |
| |
| lineState[0] = pLineObj->slicValueCache; |
| lineState[0] &= ~VP880_SS_LINE_FEED_MASK; |
| lineState[0] |= VP880_SS_ACTIVE; |
| |
| VP_LINE_STATE(VpLineCtxType, pLineCtx,
|
| ("Entering Disconnect: Channel %d: Case 2 State 0x%02X time %d", |
| pLineObj->channelId, lineState[0], pDevObj->timeStamp)); |
| |
| if (pDevObj->staticInfo.rcnPcn[VP880_RCN_LOCATION] > VP880_REV_VC) { |
| Vp880UpdateBufferChanSel(pDevObj, channelId, lineState[0], FALSE); |
| mpiIndex = VpCSLACBuildMpiBuffer(mpiIndex, mpiBuffer, VP880_DEV_MODE_WRT, |
| VP880_DEV_MODE_LEN, pDevObj->devMode); |
| } |
| |
| if (pLineObj->slicValueCache != lineState[0]) { |
| pLineObj->slicValueCache = lineState[0]; |
| mpiIndex = VpCSLACBuildMpiBuffer(mpiIndex, mpiBuffer, VP880_SYS_STATE_WRT, |
| VP880_SYS_STATE_LEN, &pLineObj->slicValueCache); |
| } |
| |
| VpMpiCmdWrapper(deviceId, pLineObj->ecVal, mpiBuffer[0], mpiIndex-1, |
| &mpiBuffer[1]); |
| mpiIndex = 0; |
| |
| pLineObj->nextSlicValue = VP880_SS_DISCONNECT; |
| pLineObj->nextSlicValue |= ((lineInTest == TRUE) ? VP880_SS_ACTIVATE_MASK : 0); |
| |
| /* Step 3: Delay, then set device to LPM. */ |
| VP_LINE_STATE(VpLineCtxType, pLineCtx,
|
| ("Delaying Disconnect Enter Channel %d at time %d from Line State 0x%08lX Other Line 0x%08lX",
|
| channelId, pDevObj->timeStamp, myDevState, devState)); |
| } else { |
| /* |
| * 3. This line is coming from any state and other line is not in LPM. |
| * Step 1: Program all ICR registers including Disable Switcher. - done |
| * Step 2: Set line to Active w/polarity (for fast discharge) |
| * Step 3: Delay, then set line to VP_LINE_DISCONNECT. - outside |
| * this if statement. |
| */ |
| |
| /* Step 2: Set line to Active w/polarity (for fast discharge) */ |
| lineState[0] = pLineObj->slicValueCache; |
| lineState[0] &= ~VP880_SS_LINE_FEED_MASK; |
| lineState[0] |= VP880_SS_ACTIVE; |
| |
| VP_LINE_STATE(VpLineCtxType, pLineCtx,
|
| ("Entering Disconnect: Channel %d: Case 3 State 0x%02X time %d", |
| pLineObj->channelId, lineState[0], pDevObj->timeStamp)); |
| |
| if (pDevObj->staticInfo.rcnPcn[VP880_RCN_LOCATION] > VP880_REV_VC) { |
| Vp880UpdateBufferChanSel(pDevObj, channelId, lineState[0], FALSE); |
| mpiIndex = VpCSLACBuildMpiBuffer(mpiIndex, mpiBuffer, VP880_DEV_MODE_WRT, |
| VP880_DEV_MODE_LEN, pDevObj->devMode); |
| } |
| |
| if (pLineObj->slicValueCache != lineState[0]) { |
| pLineObj->slicValueCache = lineState[0]; |
| mpiIndex = VpCSLACBuildMpiBuffer(mpiIndex, mpiBuffer, VP880_SYS_STATE_WRT, |
| VP880_SYS_STATE_LEN, &pLineObj->slicValueCache); |
| } |
| |
| mpiIndex = VpCSLACBuildMpiBuffer(mpiIndex, mpiBuffer, VP880_ICR3_WRT, |
| VP880_ICR3_LEN, pLineObj->icr3Values); |
| VP_LINE_STATE(VpLineCtxType, pLineCtx,
|
| ("Entering Disconnect: Channel %d: Writing ICR3 0x%02X 0x%02X 0x%02X 0x%02X time %d", |
| pLineObj->channelId, |
| pLineObj->icr3Values[0], pLineObj->icr3Values[1], |
| pLineObj->icr3Values[2], pLineObj->icr3Values[3], pDevObj->timeStamp)); |
| |
| mpiIndex = VpCSLACBuildMpiBuffer(mpiIndex, mpiBuffer, VP880_ICR4_WRT, |
| VP880_ICR4_LEN, pLineObj->icr4Values); |
| VP_LINE_STATE(VpLineCtxType, pLineCtx,
|
| ("Entering Disconnect: Channel %d: Writing ICR4 0x%02X 0x%02X 0x%02X 0x%02X time %d", |
| pLineObj->channelId, |
| pLineObj->icr4Values[0], pLineObj->icr4Values[1], |
| pLineObj->icr4Values[2], pLineObj->icr4Values[3], pDevObj->timeStamp)); |
| |
| VpMpiCmdWrapper(deviceId, pLineObj->ecVal, mpiBuffer[0], mpiIndex-1, |
| &mpiBuffer[1]); |
| mpiIndex = 0; |
| } |
| |
| if ((devState) && (!myDevState)) { |
| /* Only condition where LPM tick is handling final sequence. */ |
| } else { |
| /* Step 3: Delay, then set line to VP_LINE_DISCONNECT. */ |
| pLineObj->nextSlicValue = VP880_SS_DISCONNECT; |
| pLineObj->nextSlicValue |= ((lineInTest == TRUE) ? VP880_SS_ACTIVATE_MASK : 0); |
| |
| /* Set Discharge Time based on Supply Configuration. */ |
| pLineObj->lineTimers.timers.timer[VP_LINE_DISCONNECT_EXIT] = |
| (MS_TO_TICKRATE(Vp880SetDiscTimers(pDevObj), |
| pDevObj->devProfileData.tickRate)) | VP_ACTIVATE_TIMER; |
|
|
| /* Initialize the Disconnect Exit Timer State */
|
| pLineObj->discTimerExitState = 0; |
| |
| VP_LINE_STATE(VpLineCtxType, pLineCtx, |
| ("Chan %d Setting VP_LINE_DISCONNECT_EXIT time to %d ms (Vp880SetDiscTimers()) at time %d", |
| pLineObj->channelId, Vp880SetDiscTimers(pDevObj), pDevObj->timeStamp)); |
| } |
| |
| Vp880LLSetSysState(deviceId, pLineCtx, VP880_SS_DISCONNECT, FALSE); |
| |
| } else { /* Exiting Disconnect */ |
| VP_LINE_STATE(VpLineCtxType, pLineCtx,
|
| ("Recovering Chan %d from DISCONNECT at time %d with target value 0x%02X",
|
| channelId, pDevObj->timeStamp, nextSlicByte)); |
| |
| /* |
| * Disabling Feed and Battery Hold allows quicker/smoother transitions |
| * out of Disconnect state. |
| * |
| * Note that the non-LPM Termination Types have these speedup bits set |
| * while in Disconnect rather than setting them in the transition. |
| * Technically better to have these set while in disconnect because the |
| * setting itself requires 1 8kHz frame (125us). This minor delay isn't |
| * noticeable of course because we're transitioning from a hold |
| * condition of ~30ms to almost immediate. |
| */ |
| pLineObj->icr2Values[VP880_ICR2_SPEEDUP_INDEX] |= |
| (VP880_ICR2_MET_SPEED_CTRL | VP880_ICR2_BAT_SPEED_CTRL); |
| pLineObj->icr2Values[VP880_ICR2_SPEEDUP_INDEX+1] |= |
| (VP880_ICR2_MET_SPEED_CTRL | VP880_ICR2_BAT_SPEED_CTRL); |
| |
| pLineObj->lineTimers.timers.timer[VP_LINE_SPEEDUP_RECOVERY_TIMER] = |
| MS_TO_TICKRATE(VP880_SPEEDUP_HOLD_TIME, |
| pDevObj->devProfileData.tickRate ) | VP_ACTIVATE_TIMER; |
| |
| VP_LINE_STATE(VpLineCtxType, pLineCtx, |
| ("Chan %d Setting VP_LINE_SPEEDUP_RECOVERY_TIMER time to %d ms (VP880_SPEEDUP_HOLD_TIME) at time %d", |
| pLineObj->channelId, VP880_SPEEDUP_HOLD_TIME, pDevObj->timeStamp)); |
| |
| /* Restore Normal Line Bias Control unless changed by LPM Enter */ |
| pLineObj->icr1Values[VP880_ICR1_BIAS_OVERRIDE_LOCATION+1] |= |
| VP880_ICR1_LINE_BIAS_OVERRIDE_NORM; |
| |
| /* |
| * There are four cases to consider when exiting Disconnect: |
| * 1. This line is going to VP_LINE_STANDBY and other line is in LPM. |
| * 2. This line is going to VP_LINE_STANDBY and other line is not in LPM. |
| * 3. This line is going to non-LPM state and other line is in LPM. |
| * 4. This line is going to non-LPM state and other line is not in LPM. |
| */ |
| pLineObj->icr2Values[VP880_ICR2_VOC_DAC_INDEX] &= ~VP880_ICR2_VOC_DAC_SENSE; |
| pLineObj->icr2Values[VP880_ICR2_SWY_CTRL_INDEX] &= ~VP880_ICR2_SWY_CTRL_EN; |
| pLineObj->icr2Values[VP880_ICR2_SENSE_INDEX] &= ~VP880_ICR2_ILA_DAC; |
| |
| pLineObj->icr3Values[VP880_ICR3_LINE_CTRL_INDEX+1] |= VP880_ICR3_LINE_CTRL; |
| |
| if ((nextSlicByte == VP880_SS_IDLE) && (devState)) { |
| VP_LINE_STATE(VpLineCtxType, pLineCtx,
|
| ("Exiting Disconnect Case 1 Channel %d time %d", channelId, pDevObj->timeStamp)); |
| |
| /* |
| * 1. This line is going to VP_LINE_STANDBY and other line is in LPM. |
| * Step 1: Enable Switcher. |
| * Step 2: Set line to Active (for fast charge) |
| * Step 3: Set line to SLIC-Disconnect (end of timer) |
| */ |
| pLineObj->icr2Values[VP880_ICR2_SENSE_INDEX] = |
| (VP880_ICR2_TIP_SENSE | VP880_ICR2_RING_SENSE); |
| |
| /* |
| * This should not be required. The only way the floor voltage is |
| * not set to -70V is when one of the lines is not in LPM. But if |
| * we're exiting to LPM, the other line must either be in LPM-Standby |
| * or in Disconnect (state that allows LPM). Meaning, the switcher |
| * would not be at -70V only if this line was in a non-LPM state |
| * entering Disconnect AND the other line was in Disconnect. |
| * However, Case 2 for Entering Disconnect would occur when this line |
| * changed from OHT to Disconnect, which would cause the system to |
| * enter LPM and set the Switcher to -70V. |
| * |
| * That said, this small code makes 100% sure that if optimizations |
| * are made in the future, that the supply is at the correct voltage |
| * when the line is in LPM-Standby. |
| */ |
| if ((pDevObj->swParamsCache[VP880_FLOOR_VOLTAGE_BYTE] & 0x0D) != 0x0D) { |
| VP_LINE_STATE(VpLineCtxType, pLineCtx, |
| ("Exiting Disconnect Case 1 Switcher Adjustment Required time %d", |
| pDevObj->timeStamp)); |
| |
| pDevObj->swParamsCache[VP880_FLOOR_VOLTAGE_BYTE] &= ~VP880_FLOOR_VOLTAGE_MASK; |
| pDevObj->swParamsCache[VP880_FLOOR_VOLTAGE_BYTE] |= 0x0D; /* 70V */ |
| VP_LINE_STATE(VpDevCtxType, pDevCtx, |
| ("Exiting Disconnect Case 1 Switcher Programming: 0x%02X 0x%02X 0x%02X time %d", |
| pDevObj->swParamsCache[0], pDevObj->swParamsCache[1], pDevObj->swParamsCache[2], |
| pDevObj->timeStamp)); |
| VpMpiCmdWrapper(deviceId, ecVal, VP880_REGULATOR_PARAM_WRT, |
| VP880_REGULATOR_PARAM_LEN, pDevObj->swParamsCache); |
| /* |
| * If we have to turn on the supply here, wait longer than the normal disconnect
|
| * exit time. Normal time is based only on line stability into a 5REN load. Added
|
| * time for switcher to fully charge the supply caps/line. |
| */ |
| VpCSLACSetTimer(&pDevObj->devTimer[VP_DEV_TIMER_LP_CHANGE],
|
| (MS_TO_TICKRATE(Vp880SetDiscTimers(pDevObj), pDevObj->devProfileData.tickRate)));
|
| VP_LINE_STATE(VpDevCtxType, pDevCtx, |
| ("Exiting Disconnect Channel %d: Starting VP_DEV_TIMER_LP_CHANGE at time %d", |
| pLineObj->channelId, pDevObj->timeStamp)); |
| } |
| |
| Vp880WriteLPEnterRegisters(pLineCtx, deviceId, ecVal, lineState); |
| |
| if (pLineObj->status & VP880_LINE_LEAK) { |
| pLineObj->nextSlicValue = VP880_SS_IDLE; |
| } else { |
| pLineObj->nextSlicValue = VP880_SS_DISCONNECT; |
| /* |
| * Done entering LPM (exept for final state setting, so set |
| * line flag so the hook bit is not mis-interpreted. |
| */ |
| pLineObj->status |= VP880_LOW_POWER_EN; |
| } |
| |
| /* |
| * Set flag indicating this line could be in LPM (e.g., line state |
| * being set to VP_LINE_STANDBY) |
| */ |
| pDevObj->stateInt |= ((channelId == 0) ? VP880_LINE0_LP : VP880_LINE1_LP); |
| |
| } else if ((nextSlicByte == VP880_SS_IDLE) && (!(devState))) { |
| VP_LINE_STATE(VpLineCtxType, pLineCtx, ("Exiting Disconnect Case 2 Channel %d time %d", |
| channelId, pDevObj->timeStamp)); |
| |
| /* |
| * 2. This line is going to VP_LINE_STANDBY and other line is not in LPM. |
| * Step 1: Enable Switcher on this line |
| * Step 2: Set line to Active (for fast charge) |
| * Step 3: Set remaining ICR registers for LPM exit settings. |
| * Step 4: Set line to SLIC-IDLE (end of timer) |
| */ |
| mpiIndex = VpCSLACBuildMpiBuffer(mpiIndex, mpiBuffer, VP880_ICR2_WRT, |
| VP880_ICR2_LEN, pLineObj->icr2Values); |
| |
| VP_LINE_STATE(VpLineCtxType, pLineCtx, |
| ("LP: Case 2: Channel %d: Writing ICR2 0x%02X 0x%02X 0x%02X 0x%02X time %d", |
| pLineObj->channelId, |
| pLineObj->icr2Values[0], pLineObj->icr2Values[1], |
| pLineObj->icr2Values[2], pLineObj->icr2Values[3], pDevObj->timeStamp)); |
| |
| VP_LINE_STATE(VpLineCtxType, pLineCtx, |
| ("LP: Case 2: Setting Ch %d to State 0x%02X at time %d", |
| channelId, lineState[0], pDevObj->timeStamp)); |
| |
| if (pDevObj->staticInfo.rcnPcn[VP880_RCN_LOCATION] > VP880_REV_VC) { |
| Vp880UpdateBufferChanSel(pDevObj, channelId, lineState[0], FALSE); |
| mpiIndex = VpCSLACBuildMpiBuffer(mpiIndex, mpiBuffer, VP880_DEV_MODE_WRT, |
| VP880_DEV_MODE_LEN, pDevObj->devMode); |
| } |
| |
| if (pLineObj->slicValueCache != lineState[0]) { |
| pLineObj->slicValueCache = lineState[0]; |
| mpiIndex = VpCSLACBuildMpiBuffer(mpiIndex, mpiBuffer, VP880_SYS_STATE_WRT, |
| VP880_SYS_STATE_LEN, &pLineObj->slicValueCache); |
| } |
| |
| pLineObj->nextSlicValue = nextSlicByte; |
| pLineObj->lineState.usrCurrent = VP_LINE_STANDBY; |
| |
| VpMpiCmdWrapper(deviceId, pLineObj->ecVal, mpiBuffer[0], mpiIndex-1, |
| &mpiBuffer[1]); |
| mpiIndex = 0; |
| |
| Vp880SetLP(FALSE, pLineCtx); |
| |
| /* |
| * Set flag indicating this line could be in LPM (e.g., line state |
| * being set to VP_LINE_STANDBY) |
| */ |
| pDevObj->stateInt |= ((channelId == 0) ? VP880_LINE0_LP : VP880_LINE1_LP); |
| } else if ((nextSlicByte != VP880_SS_IDLE) && (devState)) { |
| /* |
| * 3. This line is going to non-LPM state and other line is in LPM. |
| * Step 1: Do nothing. Set the device flag to start LPM exit. |
| */ |
| VP_LINE_STATE(VpLineCtxType, pLineCtx, |
| ("Exiting Disconnect Case 3: Delaying Disconnect Exit Channel %d at time %d Slic State 0x%02X", |
| channelId, pDevObj->timeStamp, nextSlicByte));
|
| |
| /* Make sure the ICR Values are correct */
|
| Vp880SetLPRegisters(pLineObj, FALSE); |
| Vp880LLSetSysState(deviceId, pLineCtx, nextSlicByte, FALSE); |
| pLineObj->nextSlicValue = nextSlicByte; |
| } else { |
| VP_LINE_STATE(VpLineCtxType, pLineCtx, |
| ("Exiting Disconnect Case 4 Channel %d: All Non-LP time %d", |
| channelId, pDevObj->timeStamp)); |
| |
| /* |
| * 4. This line is going to non-LPM state and other line is not in LPM. |
| * Step 1: Enable Switcher on this line |
| * Step 2: Set line to Active w/Polarity (for fast charge) |
| * Step 3: Set remaining ICR registers for LPM exit settings. |
| * Step 4: Set line to new SLIC state (end of timer) |
| */ |
| Vp880SetLPRegisters(pLineObj, FALSE); /* ICR1, 3, 4 correct */ |
| |
| /* Prepare for the SLIC Line State change */ |
| lineState[0] |= (VP880_SS_POLARITY_MASK & nextSlicByte); |
| pLineObj->nextSlicValue = nextSlicByte; |
| |
| if (pDevObj->staticInfo.rcnPcn[VP880_RCN_LOCATION] > VP880_REV_VC) { |
| Vp880UpdateBufferChanSel(pDevObj, channelId, lineState[0], FALSE); |
| VpMpiCmdWrapper(deviceId, pLineObj->ecVal, VP880_DEV_MODE_WRT, |
| VP880_DEV_MODE_LEN, pDevObj->devMode); |
| } |
| |
| /* Complete LPM Exit Sequence */ |
| Vp880WriteLPExitRegisters(pLineCtx, deviceId, ecVal, lineState); |
| |
| /* |
| * Correct the device flag indicating this line could go to LPM in |
| * case being set to VP_LINE_STANDBY. |
| */ |
| if (nextSlicByte != VP880_SS_IDLE) { |
| pDevObj->stateInt &= ((channelId == 0) ? ~VP880_LINE0_LP : ~VP880_LINE1_LP); |
| } |
| } |
| |
| /* |
| * Disable LPM Exit sequence if for some reason it was previously started |
| * (e.g., application quickly went from Standby-OHT-Disconnect). When |
| * the Tracker Disable Time expires, it will set the line to Disconnect |
| * and Disable the Tracker (ICR2). While some of the steps may be what |
| * we're also trying to do, Disconnect handling is done with the |
| * Disconnect Exit (which also is Disoconnect Enter) timer. |
| */ |
| pLineObj->lineTimers.timers.timer[VP_LINE_TRACKER_DISABLE] |
| &= ~VP_ACTIVATE_TIMER; |
| } |
| |
| VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("Vp880RunLPDisc-")); |
| } |
| |
| /** |
| * Vp880LowPowerMode() |
| * This function is called when the device should be updated for Low Power |
| * mode. It determines if the device can be put into low power mode and does |
| * (if it can), sets a flag in the device object, and sets the device timer |
| * for hook debounce. |
| * |
| * Preconditions: |
| * |
| * Postconditions: |
| */ |
| void |
| Vp880LowPowerMode( |
| VpDevCtxType *pDevCtx) |
| { |
| Vp880DeviceObjectType *pDevObj = pDevCtx->pDevObj; |
| VpDeviceIdType deviceId = pDevObj->deviceId; |
| |
| VpLineCtxType *pLineCtx; |
| bool isValidCtx[VP880_MAX_NUM_CHANNELS] = {FALSE, FALSE}; |
| Vp880LineObjectType *pLineObj; |
| bool lowPower, switcherSet = FALSE; |
| |
| uint8 maxChannels = pDevObj->staticInfo.maxChannels; |
| uint8 channelId; |
| |
| #ifdef VP880_INCLUDE_TESTLINE_CODE |
| /* We don't want to interact with the line in this state */ |
| if (pDevObj->currentTest.nonIntrusiveTest == TRUE) { |
| return; |
| } |
| #endif |
| |
| /* Don't do anything if device is in calibration */ |
| if (pDevObj->state & VP_DEV_IN_CAL) { |
| return; |
| } |
| |
| /* |
| * Low Power is only possible if both lines can use low power mode. |
| * Otherwise, exit. |
| */ |
| if ((pDevObj->stateInt & (VP880_LINE0_LP | VP880_LINE1_LP)) != |
| (VP880_LINE0_LP | VP880_LINE1_LP)) { |
| lowPower = FALSE; |
| } else { |
| lowPower = TRUE; |
| } |
| |
| /* |
| * Determine which lines are valid in case we have to adjust their line |
| * states. Consider "valid" only those lines that are FXS Low Power type. |
| */ |
| for (channelId = 0; channelId < maxChannels; channelId++) { |
| if (pDevCtx->pLineCtx[channelId] != VP_NULL) { |
| pLineCtx = pDevCtx->pLineCtx[channelId]; |
| pLineObj = pLineCtx->pLineObj; |
| |
| if ((!(pLineObj->status & VP880_IS_FXO)) && |
| (VpIsLowPowerTermType(pLineObj->termType))) { |
| bool hookStatus = FALSE; |
| VpLineStateType currentState; |
| |
| isValidCtx[channelId] = TRUE; |
| VpCSLACGetLineStatus(pLineCtx, VP_INPUT_RAW_HOOK, &hookStatus); |
| VpGetLineState(pLineCtx, ¤tState); |
| |
| /* |
| * Disconnect needs to allow LPM enter for the other line. In |
| * case it was detecting off-hook prior to entering disconnect, |
| * the hook state is frozen. So need to "ignore" it. |
| */ |
| if ((currentState == VP_LINE_DISCONNECT) |
| || (pLineObj->lineTimers.timers.timer[VP_LINE_DISCONNECT_EXIT] & VP_ACTIVATE_TIMER)) { |
| hookStatus = FALSE; |
| } |
| |
| if ((Vp880IsChnlUndrTst(pDevObj, channelId) == TRUE) || |
| (pLineObj->status & VP880_LINE_LEAK) || |
| (hookStatus == TRUE)) { |
| lowPower = FALSE; |
| } |
| |
| /* |
| * Can't go directly from PolRev into LPM because the LPM feed |
| * is too weak to quickly charge up (potential) line capacitance. |
| * The Vp880SetLineStateInt() function needs to detect this |
| * transition and set the SLIC to Active in order to get the |
| * correct polarity started immediately. The PolRev Debounce |
| * timer is defined as the point when the line is stable. So |
| * it can be used to determine when LPM is ok to enter. |
| */ |
| if ((pLineObj->lineTimers.timers.timer[VP_LINE_HOOK_FREEZE] |
| & VP_ACTIVATE_TIMER) && (lowPower == TRUE)){ |
| VP_LINE_STATE(VpLineCtxType, pLineCtx, ("Delay LP Enter for PolRev at time %d", |
| pDevObj->timeStamp)); |
| return; |
| } |
| } |
| } |
| } |
| |
| if (lowPower == FALSE) { |
| /* |
| * Take the device out of low power mode and set channels to correct |
| * states. Do not affect device or channels if change has already |
| * been made. |
| */ |
| |
| for (channelId = 0; channelId < maxChannels; channelId++) { |
| if (isValidCtx[channelId] == TRUE) { |
| pLineCtx = pDevCtx->pLineCtx[channelId]; |
| pLineObj = pLineCtx->pLineObj; |
| |
| if (pLineObj->status & VP880_LOW_POWER_EN) { |
| VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("Vp880LowPowerMode+")); |
| |
| VP_LINE_STATE(VpLineCtxType, pLineCtx, ("Exit LPM for Line %d Device Status 0x%08lX Line Status 0x%04X time %d", |
| channelId, pDevObj->stateInt, pLineObj->status, pDevObj->timeStamp)); |
| |
| if (switcherSet == FALSE) { |
| VpMpiCmdWrapper(deviceId, pDevObj->ecVal, VP880_REGULATOR_PARAM_WRT, |
| VP880_REGULATOR_PARAM_LEN, pDevObj->swParams); |
| VpMemCpy(pDevObj->swParamsCache, pDevObj->swParams, |
| VP880_REGULATOR_PARAM_LEN); |
| VP_LINE_STATE(VpDevCtxType, pDevCtx, |
| ("Exiting LPM Switcher Programming: 0x%02X 0x%02X 0x%02X time %d", |
| pDevObj->swParamsCache[0], pDevObj->swParamsCache[1], pDevObj->swParamsCache[2], |
| pDevObj->timeStamp)); |
| |
| switcherSet = TRUE; |
| } |
| |
| Vp880SetLP(FALSE, pLineCtx); |
| |
| VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("Vp880LowPowerMode-")); |
| } |
| } |
| } |
| } else { |
| /* |
| * We should be in low power mode because both lines can be put into |
| * low power mode. Don't need to call Set Line State in this case for |
| * each line because there are a limited number of API-II states that |
| * can allow Low Power, and all required the SLIC state to be set to |
| * Disconnect. |
| */ |
| for (channelId = 0; channelId < maxChannels; channelId++) { |
| if (isValidCtx[channelId] == TRUE) { |
| pLineCtx = pDevCtx->pLineCtx[channelId]; |
| pLineObj = pLineCtx->pLineObj; |
| |
| if (!(pLineObj->status & VP880_LOW_POWER_EN)) { |
| VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("Vp880LowPowerMode+")); |
| |
| VP_LINE_STATE(VpLineCtxType, pLineCtx, |
| ("Enter LPM for Line %d Device Status 0x%08lX Line Status 0x%04X time %d", |
| channelId, pDevObj->stateInt, pLineObj->status, pDevObj->timeStamp)); |
| |
| if (switcherSet == FALSE) { |
| pDevObj->swParamsCache[VP880_FLOOR_VOLTAGE_BYTE] &= ~VP880_FLOOR_VOLTAGE_MASK; |
| pDevObj->swParamsCache[VP880_FLOOR_VOLTAGE_BYTE] |= 0x0D; /* 70V */ |
| VpMpiCmdWrapper(deviceId, pLineObj->ecVal, VP880_REGULATOR_PARAM_WRT, |
| VP880_REGULATOR_PARAM_LEN, pDevObj->swParamsCache); |
| VP_LINE_STATE(VpDevCtxType, pDevCtx, |
| ("Enter LPM Switcher Programming: 0x%02X 0x%02X 0x%02X time %d", |
| pDevObj->swParamsCache[0], pDevObj->swParamsCache[1], pDevObj->swParamsCache[2], |
| pDevObj->timeStamp)); |
| switcherSet = TRUE; |
| } |
| |
| Vp880SetLP(TRUE, pLineCtx); |
| VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("Vp880LowPowerMode-")); |
| } |
| } |
| } |
| } |
| } |
| |
| /** |
| * Vp880SetLP() |
| * This function modifies the line/device to enter/exit LPM. |
| * |
| * Preconditions: |
| * |
| * Postconditions: |
| */ |
| void |
| Vp880SetLP( |
| bool lpMode, |
| VpLineCtxType *pLineCtx) |
| { |
| Vp880LineObjectType *pLineObj = pLineCtx->pLineObj; |
| uint8 ecVal = pLineObj->ecVal; |
| |
| VpDevCtxType *pDevCtx = pLineCtx->pDevCtx; |
| Vp880DeviceObjectType *pDevObj = pDevCtx->pDevObj; |
| VpDeviceIdType deviceId = pDevObj->deviceId; |
| uint16 debounceTime, deviceTimer; |
| uint8 lineState[VP880_SYS_STATE_LEN]; |
| |
| VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("Vp880SetLP+")); |
| |
| /* |
| * Need timer here to allow switcher to stabilize whether entering or |
| * exiting LPM. |
| */ |
| if (pDevObj->swParams[VP880_REGULATOR_TRACK_INDEX] & VP880_REGULATOR_FIXED_RING_SWY) { |
| /* |
| * Longest is using "fixed" ringing mode because the external |
| * capacitors are generally very large. |
| */ |
| debounceTime = VP880_PWR_SWITCH_DEBOUNCE_FXT; |
| } else { |
| debounceTime = VP880_PWR_SWITCH_DEBOUNCE_FUT; |
| } |
| deviceTimer = MS_TO_TICKRATE(debounceTime, pDevObj->devProfileData.tickRate); |
| |
| /* |
| * If the Disconnect Exit Timer is active, make sure the power timer waits |
| * until after the Disconnect Exit Timer expires. |
| */ |
| if (pLineObj->lineTimers.timers.timer[VP_LINE_DISCONNECT_EXIT] & VP_ACTIVATE_TIMER) { |
| uint16 currentDiscTime = |
| pLineObj->lineTimers.timers.timer[VP_LINE_DISCONNECT_EXIT] & ~VP_ACTIVATE_TIMER; |
| deviceTimer = ((currentDiscTime < deviceTimer) ? deviceTimer : (currentDiscTime + 1)); |
| } |
| |
| /* |
| * In case this timer is being modified by both lines (corner case of state change timing and
|
| * seqence), make sure a previous longer value is not reduced. |
| */ |
| VpCSLACSetTimer(&pDevObj->devTimer[VP_DEV_TIMER_LP_CHANGE], deviceTimer);
|
| VP_LINE_STATE(VpDevCtxType, pDevCtx, |
| ("Vp880SetLP() Channel %d: Starting VP_DEV_TIMER_LP_CHANGE with %d ms at time %d", |
| pLineObj->channelId,
|
| TICKS_TO_MS((pDevObj->devTimer[VP_DEV_TIMER_LP_CHANGE] & ~VP_ACTIVATE_TIMER),
|
| pDevObj->devProfileData.tickRate), pDevObj->timeStamp)); |
|
|
| if (lpMode == FALSE) { |
| VP_LINE_STATE(VpLineCtxType, pLineCtx,
|
| ("Taking Channel %d out of Low Power Mode at time %d User State %d",
|
| pLineObj->channelId, pDevObj->timeStamp, pLineObj->lineState.usrCurrent)); |
| |
| pLineObj->status &= ~VP880_LOW_POWER_EN; |
| |
| if (pLineObj->lineState.usrCurrent != VP_LINE_DISCONNECT) { |
| bool internalApiOperation = FALSE; |
| |
| Vp880SetLPRegisters(pLineObj, FALSE); |
| |
| #ifdef VP_CSLAC_SEQ_EN |
| if ((pLineObj->cadence.status & (VP_CADENCE_STATUS_ACTIVE | VP_CADENCE_STATUS_SENDSIG)) == |
| (VP_CADENCE_STATUS_ACTIVE | VP_CADENCE_STATUS_SENDSIG)) { |
| internalApiOperation = TRUE; |
| } |
| #endif |
| |
| if ((pLineObj->lineState.usrCurrent == VP_LINE_STANDBY) |
| && (internalApiOperation == FALSE)) { |
| lineState[0] = VP880_SS_ACTIVE; |
| Vp880WriteLPExitRegisters(pLineCtx, deviceId, ecVal, lineState); |
| pLineObj->nextSlicValue = VP880_SS_IDLE; |
| } else { |
| Vp880WriteLPExitRegisters(pLineCtx, deviceId, ecVal, VP_NULL); |
| } |
| } |
| |
| /* |
| * Disable LPM Exit sequence. When the Tracker Disable Time expires, it |
| * will set the line to Disconnect and Disable the Tracker (ICR2). |
| * Obviously, we no longer want to do this. |
| */ |
| pLineObj->lineTimers.timers.timer[VP_LINE_TRACKER_DISABLE] |
| &= ~VP_ACTIVATE_TIMER; |
| } else { |
| VP_LINE_STATE(VpLineCtxType, pLineCtx, ("Putting Channel %d in Low Power Mode at time %d", |
| pLineObj->channelId, pDevObj->timeStamp)); |
| |
| pLineObj->status |= VP880_LOW_POWER_EN; |
| |
| if (pLineObj->lineState.usrCurrent != VP_LINE_DISCONNECT) { |
| /* Set timer to wait before making final changes. When this timer |
| * expires, the following sequence is run: |
| * |
| * - Set SLIC state to Disconnect |
| * - Write the following: |
| * icr2[VP880_ICR2_VOC_DAC_INDEX] |= VP880_ICR2_ILA_DAC; |
| * icr2[VP880_ICR2_VOC_DAC_INDEX] &= ~VP880_ICR2_VOC_DAC_SENSE; |
| * icr2[VP880_ICR2_VOC_DAC_INDEX+1] &= ~VP880_ICR2_ILA_DAC; |
| */ |
| pLineObj->lineTimers.timers.timer[VP_LINE_TRACKER_DISABLE] = |
| (MS_TO_TICKRATE(VP880_TRACKER_DISABLE_TIME, |
| pDevObj->devProfileData.tickRate)) | VP_ACTIVATE_TIMER; |
| |
| /* |
| * We are entering LPM into VP_LINE_STANDBY. So the required ICR |
| * values are not yet set in the line object (as they would be if |
| * entering from VP_LINE_DISCONNECT). |
| */ |
| lineState[0] = VP880_SS_DISCONNECT; |
| |
| Vp880SetLPRegisters(pLineObj, TRUE); |
| } else { |
| uint16 dischargeTime = Vp880SetDiscTimers(pDevObj); |
| |
| /* |
| * We are entering LPM into VP_LINE_DISCONNECT. So the required ICR |
| * values have been set in the line object, just need to force the |
| * required sequence (Active w/polarity, then Disconnect). |
| */ |
| lineState[0] = VP880_SS_ACTIVE; |
| pLineObj->nextSlicValue = VP880_SS_DISCONNECT; |
| |
| /* Set Discharge Time based on Supply Configuration. */ |
| if (dischargeTime < VP880_TRACKER_DISABLE_TIME) { |
| dischargeTime = VP880_TRACKER_DISABLE_TIME; |
| } |
| |
| /* Set timer to wait before making final changes. When this timer |
| * expires, the following sequence is run: |
| * |
| * - Set SLIC state to Disconnect |
| * - Write the following: |
| * icr2[VP880_ICR2_VOC_DAC_INDEX] |= VP880_ICR2_ILA_DAC; |
| * icr2[VP880_ICR2_VOC_DAC_INDEX] &= ~VP880_ICR2_VOC_DAC_SENSE; |
| * icr2[VP880_ICR2_VOC_DAC_INDEX+1] &= ~VP880_ICR2_ILA_DAC; |
| */ |
| pLineObj->lineTimers.timers.timer[VP_LINE_DISCONNECT_EXIT] = |
| (MS_TO_TICKRATE(dischargeTime, pDevObj->devProfileData.tickRate)) |
| | VP_ACTIVATE_TIMER;
|
|
|
| /* Initialize the Disconnect Exit Timer State */
|
| pLineObj->discTimerExitState = 0; |
| |
| VP_LINE_STATE(VpLineCtxType, pLineCtx, |
| ("Chan %d Setting VP_LINE_DISCONNECT_EXIT time to %d ms (dischargeTime) at time %d", |
| pLineObj->channelId, dischargeTime, pDevObj->timeStamp)); |
| } |
| |
| Vp880WriteLPEnterRegisters(pLineCtx, deviceId, ecVal, |
| lineState); |
| } |
| VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("Vp880SetLP-")); |
| } |
| |
| /** |
| * Vp880WriteLPExitRegisters() |
| * This function writes the ICR and State values to the device for LPM exit. |
| * |
| * Preconditions: |
| * None. Modification of line object data only. |
| * |
| * Postconditions: |
| * The device registers have been modified. |
| */ |
| void |
| Vp880WriteLPExitRegisters( |
| VpLineCtxType *pLineCtx, |
| VpDeviceIdType deviceId, |
| uint8 ecVal, |
| uint8 *lineState) |
| { |
| VpDevCtxType *pDevCtx = pLineCtx->pDevCtx; |
| Vp880DeviceObjectType *pDevObj = pDevCtx->pDevObj; |
| Vp880LineObjectType *pLineObj = pLineCtx->pLineObj; |
| uint8 mpiBuffer[6 + VP880_DEV_MODE_LEN + VP880_SYS_STATE_LEN + |
| VP880_ICR1_LEN + VP880_ICR2_LEN + VP880_ICR3_LEN + |
| VP880_ICR4_LEN]; |
| uint8 mpiIndex = 0; |
| |
| if (pLineObj->lineState.currentState == VP_LINE_TIP_OPEN) { |
| Vp880SetLineStateInt(pLineCtx, pLineObj->lineState.currentState); |
| return; |
| } |
| |
| mpiIndex = VpCSLACBuildMpiBuffer(mpiIndex, mpiBuffer, VP880_ICR3_WRT, |
| VP880_ICR3_LEN, pLineObj->icr3Values); |
| |
| mpiIndex = VpCSLACBuildMpiBuffer(mpiIndex, mpiBuffer, VP880_ICR4_WRT, |
| VP880_ICR4_LEN, pLineObj->icr4Values); |
| |
| if (lineState == VP_NULL) { |
| VP_LINE_STATE(VpLineCtxType, pLineCtx,
|
| ("LP Exit: Writing ICR3 0x%02X 0x%02X 0x%02X 0x%02X on Ch %d time %d", |
| pLineObj->icr3Values[0], pLineObj->icr3Values[1], |
| pLineObj->icr3Values[2], pLineObj->icr3Values[3],
|
| pLineObj->channelId, pDevObj->timeStamp)); |
| |
| VP_LINE_STATE(VpLineCtxType, pLineCtx,
|
| ("LP Exit: Writing ICR4 0x%02X 0x%02X 0x%02X 0x%02X on Ch %d time %d", |
| pLineObj->icr4Values[0], pLineObj->icr4Values[1], |
| pLineObj->icr4Values[2], pLineObj->icr4Values[3],
|
| pLineObj->channelId, pDevObj->timeStamp)); |
| |
| VP_LINE_STATE(VpLineCtxType, pLineCtx,
|
| ("LP Exit: Writing LineState %d on Ch %d time %d", |
| pLineObj->lineState.currentState, pLineObj->channelId, pDevObj->timeStamp)); |
| |
| VpMpiCmdWrapper(deviceId, pLineObj->ecVal, mpiBuffer[0], mpiIndex-1, |
| &mpiBuffer[1]); |
| mpiIndex = 0; |
| |
| Vp880SetLineStateInt(pLineCtx, pLineObj->lineState.currentState); |
| } else { |
| if (pDevObj->staticInfo.rcnPcn[VP880_RCN_LOCATION] > VP880_REV_VC) { |
| Vp880UpdateBufferChanSel(pLineCtx->pDevCtx->pDevObj, pLineObj->channelId, |
| lineState[0], FALSE); |
| mpiIndex = VpCSLACBuildMpiBuffer(mpiIndex, mpiBuffer, VP880_DEV_MODE_WRT, |
| VP880_DEV_MODE_LEN, pDevObj->devMode); |
| } |
| |
| if (pLineObj->slicValueCache != lineState[0]) { |
| VP_LINE_STATE(VpLineCtxType, pLineCtx,
|
| ("LP Exit: Writing SLIC State 0x%02X on Ch %d time %d", |
| lineState[0], pLineObj->channelId, pDevObj->timeStamp)); |
| |
| pLineObj->slicValueCache = lineState[0]; |
| mpiIndex = VpCSLACBuildMpiBuffer(mpiIndex, mpiBuffer, VP880_SYS_STATE_WRT, |
| VP880_SYS_STATE_LEN, &pLineObj->slicValueCache); |
| } |
| } |
| |
| mpiIndex = Vp880ProtectedWriteICR1(pLineObj, mpiIndex, mpiBuffer); |
| |
| mpiIndex = VpCSLACBuildMpiBuffer(mpiIndex, mpiBuffer, VP880_ICR2_WRT, |
| VP880_ICR2_LEN, pLineObj->icr2Values); |
| |
| VpMpiCmdWrapper(deviceId, pLineObj->ecVal, mpiBuffer[0], mpiIndex-1, |
| &mpiBuffer[1]); |
| |
| if (lineState != VP_NULL) { |
| VP_LINE_STATE(VpLineCtxType, pLineCtx,
|
| ("LP Exit: Writing ICR3 0x%02X 0x%02X 0x%02X 0x%02X on Ch %d time %d",
|
| pLineObj->icr3Values[0], pLineObj->icr3Values[1],
|
| pLineObj->icr3Values[2], pLineObj->icr3Values[3],
|
| pLineObj->channelId, pDevObj->timeStamp)); |
| |
| VP_LINE_STATE(VpLineCtxType, pLineCtx,
|
| ("LP Exit: Writing ICR4 0x%02X 0x%02X 0x%02X 0x%02X on Ch %d time %d",
|
| pLineObj->icr4Values[0], pLineObj->icr4Values[1],
|
| pLineObj->icr4Values[2], pLineObj->icr4Values[3],
|
| pLineObj->channelId, pDevObj->timeStamp)); |
| } |
| |
| VP_LINE_STATE(VpLineCtxType, pLineCtx,
|
| ("LP Exit: Writing ICR2 0x%02X 0x%02X 0x%02X 0x%02X on Ch %d time %d", |
| pLineObj->icr2Values[0], pLineObj->icr2Values[1], |
| pLineObj->icr2Values[2], pLineObj->icr2Values[3],
|
| pLineObj->channelId, pDevObj->timeStamp)); |
| } |
| |
| /** |
| * Vp880WriteLPEnterRegisters() |
| * This function writes the ICR and State values to the device for LPM enter. |
| * |
| * Preconditions: |
| * None. Modification of line object data only. |
| * |
| * Postconditions: |
| * The device registers have been modified. |
| */ |
| void |
| Vp880WriteLPEnterRegisters( |
| VpLineCtxType *pLineCtx, |
| VpDeviceIdType deviceId, |
| uint8 ecVal, |
| uint8 *lineState) |
| { |
| VpDevCtxType *pDevCtx = pLineCtx->pDevCtx; |
| Vp880DeviceObjectType *pDevObj = pDevCtx->pDevObj; |
| Vp880LineObjectType *pLineObj = pLineCtx->pLineObj; |
| uint8 mpiBuffer[6 + VP880_DEV_MODE_LEN + VP880_SYS_STATE_LEN + |
| VP880_ICR1_LEN + VP880_ICR2_LEN + VP880_ICR3_LEN + |
| VP880_ICR4_LEN]; |
| uint8 mpiIndex = 0; |
| |
| mpiIndex = Vp880ProtectedWriteICR1(pLineObj, mpiIndex, mpiBuffer); |
| |
| mpiIndex = VpCSLACBuildMpiBuffer(mpiIndex, mpiBuffer, VP880_ICR2_WRT, |
| VP880_ICR2_LEN, pLineObj->icr2Values); |
| |
| if (pDevObj->staticInfo.rcnPcn[VP880_RCN_LOCATION] > VP880_REV_VC) { |
| Vp880UpdateBufferChanSel(pLineCtx->pDevCtx->pDevObj, pLineObj->channelId, |
| lineState[0], FALSE); |
| mpiIndex = VpCSLACBuildMpiBuffer(mpiIndex, mpiBuffer, VP880_DEV_MODE_WRT, |
| VP880_DEV_MODE_LEN, pDevObj->devMode); |
| } |
| |
| pLineObj->slicValueCache = lineState[0]; |
| mpiIndex = VpCSLACBuildMpiBuffer(mpiIndex, mpiBuffer, VP880_SYS_STATE_WRT, |
| VP880_SYS_STATE_LEN, &pLineObj->slicValueCache); |
| |
| mpiIndex = VpCSLACBuildMpiBuffer(mpiIndex, mpiBuffer, VP880_ICR3_WRT, |
| VP880_ICR3_LEN, pLineObj->icr3Values); |
| |
| mpiIndex = VpCSLACBuildMpiBuffer(mpiIndex, mpiBuffer, VP880_ICR4_WRT, |
| VP880_ICR4_LEN, pLineObj->icr4Values); |
| |
| VpMpiCmdWrapper(deviceId, pLineObj->ecVal, mpiBuffer[0], mpiIndex-1, |
| &mpiBuffer[1]); |
| |
| VP_LINE_STATE(VpLineCtxType, pLineCtx, |
| ("LP Enter: Writing ICR2 0x%02X 0x%02X 0x%02X 0x%02X on Ch %d time %d", |
| pLineObj->icr2Values[0], pLineObj->icr2Values[1], |
| pLineObj->icr2Values[2], pLineObj->icr2Values[3], |
| pLineObj->channelId, pDevObj->timeStamp)); |
| |
| /* Set line to desired state */ |
| VP_LINE_STATE(VpLineCtxType, pLineCtx, |
| ("LP Enter: Setting State 0x%02X on Ch %d time %d", |
| lineState[0], pLineObj->channelId, pDevObj->timeStamp)); |
| |
| VP_LINE_STATE(VpLineCtxType, pLineCtx, |
| ("LP Enter: Writing ICR3 0x%02X 0x%02X 0x%02X 0x%02X on Ch %d time %d", |
| pLineObj->icr3Values[0], pLineObj->icr3Values[1], |
| pLineObj->icr3Values[2], pLineObj->icr3Values[3], |
| pLineObj->channelId, pDevObj->timeStamp)); |
| |
| VP_LINE_STATE(VpLineCtxType, pLineCtx, |
| ("LP Enter: Writing ICR4 0x%02X 0x%02X 0x%02X 0x%02X on Ch %d time %d", |
| pLineObj->icr4Values[0], pLineObj->icr4Values[1], |
| pLineObj->icr4Values[2], pLineObj->icr4Values[3], |
| pLineObj->channelId, pDevObj->timeStamp)); |
| } |
| |
| /** |
| * Vp880SetLPRegisters() |
| * This function modifies the line object ICR register values. It does not |
| * write to the device. |
| * |
| * Preconditions: |
| * None. Modification of line object data only. |
| * |
| * Postconditions: |
| * The line object data (ICR values) have been modified. |
| */ |
| void |
| Vp880SetLPRegisters( |
| Vp880LineObjectType *pLineObj, |
| bool lpModeTo) |
| { |
| VP_API_FUNC_INT(None, VP_NULL, ("Vp880SetLPRegisters+")); |
| |
| if (lpModeTo == TRUE) { |
| pLineObj->icr1Values[VP880_ICR1_BIAS_OVERRIDE_LOCATION] =
|
| VP880_ICR1_LINE_BIAS_OVERRIDE; |
| pLineObj->icr1Values[VP880_ICR1_BIAS_OVERRIDE_LOCATION+1] =
|
| VP880_ICR1_LINE_BIAS_OVERRIDE_NORM; |
| |
| pLineObj->icr1Values[VP880_ICR1_RING_AND_DAC_LOCATION] &= |
| ~VP880_ICR1_RING_BIAS_DAC_MASK; |
| pLineObj->icr1Values[VP880_ICR1_RING_AND_DAC_LOCATION+1] &= |
| ~VP880_ICR1_RING_BIAS_DAC_MASK; |
| |
| pLineObj->icr2Values[VP880_ICR2_SENSE_INDEX] = |
| (VP880_ICR2_DAC_SENSE | VP880_ICR2_FEED_SENSE |
|
| VP880_ICR2_TIP_SENSE | VP880_ICR2_RING_SENSE); |
| |
| pLineObj->icr2Values[VP880_ICR2_SENSE_INDEX+1] = |
| (VP880_ICR2_FEED_SENSE | VP880_ICR2_TIP_SENSE | VP880_ICR2_RING_SENSE); |
| |
| /* |
| * Preclear all controls except SWY, Battery Speedup and DC Feed |
| * Speedup (set if exiting Disconnect) |
| */ |
| pLineObj->icr2Values[VP880_ICR2_SWY_CTRL_INDEX] &= |
| (VP880_ICR2_SWY_LIM_CTRL1 | VP880_ICR2_SWY_LIM_CTRL |
| | VP880_ICR2_MET_SPEED_CTRL | VP880_ICR2_BAT_SPEED_CTRL); |
| |
| /* Make sure SWY controls are set */ |
| pLineObj->icr2Values[VP880_ICR2_SWY_CTRL_INDEX] |= |
| (VP880_ICR2_SWY_LIM_CTRL1 | VP880_ICR2_SWY_LIM_CTRL); |
| pLineObj->icr2Values[VP880_ICR2_SWY_CTRL_INDEX+1] |= |
| (VP880_ICR2_SWY_LIM_CTRL1); |
| |
| pLineObj->icr3Values[VP880_ICR3_LINE_CTRL_INDEX] |= VP880_ICR3_LINE_CTRL; |
| pLineObj->icr3Values[VP880_ICR3_LINE_CTRL_INDEX+1] |= VP880_ICR3_LINE_CTRL; |
| |
| pLineObj->icr4Values[VP880_ICR4_SUP_INDEX] |= |
| (VP880_ICR4_SUP_DAC_CTRL | VP880_ICR4_SUP_DET_CTRL | VP880_ICR4_SUP_POL_CTRL); |
| pLineObj->icr4Values[VP880_ICR4_SUP_INDEX+1] |= |
| (VP880_ICR4_SUP_DAC_CTRL | VP880_ICR4_SUP_DET_CTRL | VP880_ICR4_SUP_POL_CTRL); |
| } else { |
| pLineObj->icr3Values[VP880_ICR3_LINE_CTRL_INDEX] &= ~VP880_ICR3_LINE_CTRL; |
| |
| pLineObj->icr4Values[VP880_ICR4_SUP_INDEX] &= |
| (uint8)(~(VP880_ICR4_SUP_DAC_CTRL | VP880_ICR4_SUP_DET_CTRL | VP880_ICR4_SUP_POL_CTRL)); |
| |
| /* Remove previously set SW control of ICR1 */ |
| pLineObj->icr1Values[VP880_ICR1_BIAS_OVERRIDE_LOCATION] &= ~VP880_ICR1_LINE_BIAS_OVERRIDE; |
| |
| pLineObj->icr2Values[VP880_ICR2_SENSE_INDEX] &= |
| (uint8)(~(VP880_ICR2_RING_SENSE | VP880_ICR2_TIP_SENSE |
|
| VP880_ICR2_DAC_SENSE | VP880_ICR2_FEED_SENSE)); |
| } |
| |
| VP_LINE_STATE(None, VP_NULL, ("LP %s Cache: ICR1 0x%02X 0x%02X 0x%02X 0x%02X on Ch %d", |
| ((lpModeTo == TRUE) ? "Enter" : "Exit"), |
| pLineObj->icr1Values[0], pLineObj->icr1Values[1], |
| pLineObj->icr1Values[2], pLineObj->icr1Values[3], pLineObj->channelId)); |
| |
| VP_LINE_STATE(None, VP_NULL, ("LP %s Cache: ICR2 0x%02X 0x%02X 0x%02X 0x%02X on Ch %d", |
| ((lpModeTo == TRUE) ? "Enter" : "Exit"), |
| pLineObj->icr2Values[0], pLineObj->icr2Values[1], |
| pLineObj->icr2Values[2], pLineObj->icr2Values[3], pLineObj->channelId)); |
| |
| VP_LINE_STATE(None, VP_NULL, ("LP %s Cache: ICR3 0x%02X 0x%02X 0x%02X 0x%02X on Ch %d", |
| ((lpModeTo == TRUE) ? "Enter" : "Exit"), |
| pLineObj->icr3Values[0], pLineObj->icr3Values[1], |
| pLineObj->icr3Values[2], pLineObj->icr3Values[3], pLineObj->channelId)); |
| |
| VP_LINE_STATE(None, VP_NULL, ("LP %s Cache: ICR4 0x%02X 0x%02X 0x%02X 0x%02X on Ch %d", |
| ((lpModeTo == TRUE) ? "Enter" : "Exit"), |
| pLineObj->icr4Values[0], pLineObj->icr4Values[1], |
| pLineObj->icr4Values[2], pLineObj->icr4Values[3], pLineObj->channelId)); |
| |
| VP_API_FUNC_INT(None, VP_NULL, ("Vp880SetLPRegisters-")); |
| } |
| |
| #endif |