/******************************************************************************
 *
 * Name:	skdim.c
 * Project:	GEnesis, PCI Gigabit Ethernet Adapter
 * Version:	$Revision: 1.5 $
 * Date:	$Date: 2003/11/28 12:55:40 $
 * Purpose:	All functions to maintain interrupt moderation
 *
 ******************************************************************************/

/******************************************************************************
 *
 *	(C)Copyright 1998-2002 SysKonnect GmbH.
 *	(C)Copyright 2002-2003 Marvell.
 *
 *	This program is free software; you can redistribute it and/or modify
 *	it under the terms of the GNU General Public License as published by
 *	the Free Software Foundation; either version 2 of the License, or
 *	(at your option) any later version.
 *
 *	The information in this file is provided "AS IS" without warranty.
 *
 ******************************************************************************/

/******************************************************************************
 *
 * History:
 *	
 *	$Log: skdim.c,v $
 *	Revision 1.5  2003/11/28 12:55:40  rroesler
 *	Fix: support for new process timing interface added
 *	
 *	Revision 1.4  2003/10/10 10:58:56  mlindner
 *	Fix: CPU detection under the kernel 2.6
 *	
 *	Revision 1.3  2003/10/07 08:17:08  mlindner
 *	Fix: Copyright changes
 *	
 *	Revision 1.2  2003/08/21 12:35:05  mlindner
 *	Fix: Corrected CPU detection and compile errors on single CPU machines
 *	
 *	Revision 1.1  2003/07/18 13:39:55  rroesler
 *	Fix: Re-enter after CVS crash
 *	
 *	Revision 1.4  2003/07/07 09:45:47  rroesler
 *	Fix: Compiler warnings corrected
 *	
 *	Revision 1.3  2003/06/10 09:16:40  rroesler
 *	Adapt GetCurrentSystemLoad() to NOT access the kernels
 *	kstat-structure in kernel 2.5/2.6. This must be done
 *	due to a not exported symbol. Instead of evaluating the
 *	SystemLoad directly, the nbr of interrupts is used as
 *	a rough basis for the load.
 *	
 *
 *
 ******************************************************************************/

/******************************************************************************
 *
 * Description:
 *
 * This module is intended to manage the dynamic interrupt moderation on both   
 * GEnesis and Yukon adapters.
 *
 * Include File Hierarchy:
 *
 *	"skdrv1st.h"
 *	"skdrv2nd.h"
 *
 ******************************************************************************/

#ifndef	lint
static const char SysKonnectFileId[] =
	"@(#) $Id: skdim.c,v 1.5 2003/11/28 12:55:40 rroesler Exp $ (C) SysKonnect.";
#endif

#define __SKADDR_C

#ifdef __cplusplus
#error C++ is not yet supported.
extern "C" {
#endif

/*******************************************************************************
**
** Includes
**
*******************************************************************************/

#ifndef __INC_SKDRV1ST_H
#include "h/skdrv1st.h"
#endif

#ifndef __INC_SKDRV2ND_H
#include "h/skdrv2nd.h"
#endif

#include	<linux/kernel_stat.h>

/*******************************************************************************
**
** Defines
**
*******************************************************************************/

/*******************************************************************************
**
** Typedefs
**
*******************************************************************************/

/*******************************************************************************
**
** Local function prototypes 
**
*******************************************************************************/

static unsigned int GetCurrentSystemLoad(SK_AC *pAC);
static SK_U64       GetIsrCalls(SK_AC *pAC);
static SK_BOOL      IsIntModEnabled(SK_AC *pAC);
static void         SetCurrIntCtr(SK_AC *pAC);
static void         EnableIntMod(SK_AC *pAC); 
static void         DisableIntMod(SK_AC *pAC);
static void         ResizeDimTimerDuration(SK_AC *pAC);
static void         DisplaySelectedModerationType(SK_AC *pAC);
static void         DisplaySelectedModerationMask(SK_AC *pAC);
static void         DisplayDescrRatio(SK_AC *pAC);

/*******************************************************************************
**
** Global variables
**
*******************************************************************************/

/*******************************************************************************
**
** Local variables
**
*******************************************************************************/

/*******************************************************************************
**
** Global functions 
**
*******************************************************************************/

/*******************************************************************************
** Function     : SkDimModerate
** Description  : Called in every ISR to check if moderation is to be applied
**                or not for the current number of interrupts
** Programmer   : Ralph Roesler
** Last Modified: 22-mar-03
** Returns      : void (!)
** Notes        : -
*******************************************************************************/

void 
SkDimModerate(SK_AC *pAC) {
    unsigned int CurrSysLoad    = 0;  /* expressed in percent */
    unsigned int LoadIncrease   = 0;  /* expressed in percent */
    SK_U64       ThresholdInts  = 0;
    SK_U64       IsrCallsPerSec = 0;

#define M_DIMINFO pAC->DynIrqModInfo

    if (!IsIntModEnabled(pAC)) {
        if (M_DIMINFO.IntModTypeSelect == C_INT_MOD_DYNAMIC) {
            CurrSysLoad = GetCurrentSystemLoad(pAC);
            if (CurrSysLoad > 75) {
                    /* 
                    ** More than 75% total system load! Enable the moderation 
                    ** to shield the system against too many interrupts.
                    */
                    EnableIntMod(pAC);
            } else if (CurrSysLoad > M_DIMINFO.PrevSysLoad) {
                LoadIncrease = (CurrSysLoad - M_DIMINFO.PrevSysLoad);
                if (LoadIncrease > ((M_DIMINFO.PrevSysLoad *
                                         C_INT_MOD_ENABLE_PERCENTAGE) / 100)) {
                    if (CurrSysLoad > 10) {
                        /* 
                        ** More than 50% increase with respect to the 
                        ** previous load of the system. Most likely this 
                        ** is due to our ISR-proc...
                        */
                        EnableIntMod(pAC);
                    }
                }
            } else {
                /*
                ** Neither too much system load at all nor too much increase
                ** with respect to the previous system load. Hence, we can leave
                ** the ISR-handling like it is without enabling moderation.
                */
            }
            M_DIMINFO.PrevSysLoad = CurrSysLoad;
        }   
    } else {
        if (M_DIMINFO.IntModTypeSelect == C_INT_MOD_DYNAMIC) {
            ThresholdInts  = ((M_DIMINFO.MaxModIntsPerSec *
                                   C_INT_MOD_DISABLE_PERCENTAGE) / 100);
            IsrCallsPerSec = GetIsrCalls(pAC);
            if (IsrCallsPerSec <= ThresholdInts) {
                /* 
                ** The number of interrupts within the last second is 
                ** lower than the disable_percentage of the desried 
                ** maxrate. Therefore we can disable the moderation.
                */
                DisableIntMod(pAC);
                M_DIMINFO.MaxModIntsPerSec = 
                   (M_DIMINFO.MaxModIntsPerSecUpperLimit +
                    M_DIMINFO.MaxModIntsPerSecLowerLimit) / 2;
            } else {
                /*
                ** The number of interrupts per sec is the same as expected.
                ** Evalulate the descriptor-ratio. If it has changed, a resize 
                ** in the moderation timer might be usefull
                */
                if (M_DIMINFO.AutoSizing) {
                    ResizeDimTimerDuration(pAC);
                }
            }
        }
    }

    /*
    ** Some information to the log...
    */
    if (M_DIMINFO.DisplayStats) {
        DisplaySelectedModerationType(pAC);
        DisplaySelectedModerationMask(pAC);
        DisplayDescrRatio(pAC);
    }

    M_DIMINFO.NbrProcessedDescr = 0; 
    SetCurrIntCtr(pAC);
}

/*******************************************************************************
** Function     : SkDimStartModerationTimer
** Description  : Starts the audit-timer for the dynamic interrupt moderation
** Programmer   : Ralph Roesler
** Last Modified: 22-mar-03
** Returns      : void (!)
** Notes        : -
*******************************************************************************/

void 
SkDimStartModerationTimer(SK_AC *pAC) {
    SK_EVPARA    EventParam;   /* Event struct for timer event */
 
    SK_MEMSET((char *) &EventParam, 0, sizeof(EventParam));
    EventParam.Para32[0] = SK_DRV_MODERATION_TIMER;
    SkTimerStart(pAC, pAC->IoBase, &pAC->DynIrqModInfo.ModTimer,
                 SK_DRV_MODERATION_TIMER_LENGTH,
                 SKGE_DRV, SK_DRV_TIMER, EventParam);
}

/*******************************************************************************
** Function     : SkDimEnableModerationIfNeeded
** Description  : Either enables or disables moderation
** Programmer   : Ralph Roesler
** Last Modified: 22-mar-03
** Returns      : void (!)
** Notes        : This function is called when a particular adapter is opened
**                There is no Disable function, because when all interrupts 
**                might be disable, the moderation timer has no meaning at all
******************************************************************************/

void
SkDimEnableModerationIfNeeded(SK_AC *pAC) {

    if (M_DIMINFO.IntModTypeSelect == C_INT_MOD_STATIC) {
        EnableIntMod(pAC);   /* notification print in this function */
    } else if (M_DIMINFO.IntModTypeSelect == C_INT_MOD_DYNAMIC) {
        SkDimStartModerationTimer(pAC);
        if (M_DIMINFO.DisplayStats) {
            printk("Dynamic moderation has been enabled\n");
        }
    } else {
        if (M_DIMINFO.DisplayStats) {
            printk("No moderation has been enabled\n");
        }
    }
}

/*******************************************************************************
** Function     : SkDimDisplayModerationSettings
** Description  : Displays the current settings regaring interrupt moderation
** Programmer   : Ralph Roesler
** Last Modified: 22-mar-03
** Returns      : void (!)
** Notes        : -
*******************************************************************************/

void 
SkDimDisplayModerationSettings(SK_AC *pAC) {
    DisplaySelectedModerationType(pAC);
    DisplaySelectedModerationMask(pAC);
}

/*******************************************************************************
**
** Local functions 
**
*******************************************************************************/

/*******************************************************************************
** Function     : GetCurrentSystemLoad
** Description  : Retrieves the current system load of the system. This load
**                is evaluated for all processors within the system.
** Programmer   : Ralph Roesler
** Last Modified: 22-mar-03
** Returns      : unsigned int: load expressed in percentage
** Notes        : The possible range being returned is from 0 up to 100.
**                Whereas 0 means 'no load at all' and 100 'system fully loaded'
**                It is impossible to determine what actually causes the system
**                to be in 100%, but maybe that is due to too much interrupts.
*******************************************************************************/

static unsigned int
GetCurrentSystemLoad(SK_AC *pAC) {
	unsigned long jif         = jiffies;
	unsigned int  UserTime    = 0;
	unsigned int  SystemTime  = 0;
	unsigned int  NiceTime    = 0;
	unsigned int  IdleTime    = 0;
	unsigned int  TotalTime   = 0;
	unsigned int  UsedTime    = 0;
	unsigned int  SystemLoad  = 0;

	/* unsigned int  NbrCpu      = 0; */

	/*
	** The following lines have been commented out, because
	** from kernel 2.5.44 onwards, the kernel-owned structure
	**
	**      struct kernel_stat kstat
	**
	** is not marked as an exported symbol in the file
	**
	**      kernel/ksyms.c 
	**
	** As a consequence, using this driver as KLM is not possible
	** and any access of the structure kernel_stat via the 
	** dedicated macros kstat_cpu(i).cpustat.xxx is to be avoided.
	**
	** The kstat-information might be added again in future 
	** versions of the 2.5.xx kernel, but for the time being, 
	** number of interrupts will serve as indication how much 
	** load we currently have... 
	**
	** for (NbrCpu = 0; NbrCpu < num_online_cpus(); NbrCpu++) {
	**	UserTime   = UserTime   + kstat_cpu(NbrCpu).cpustat.user;
	**	NiceTime   = NiceTime   + kstat_cpu(NbrCpu).cpustat.nice;
	**	SystemTime = SystemTime + kstat_cpu(NbrCpu).cpustat.system;
	** }
	*/
	SK_U64 ThresholdInts  = 0;
	SK_U64 IsrCallsPerSec = 0;

	ThresholdInts  = ((M_DIMINFO.MaxModIntsPerSec *
			   C_INT_MOD_ENABLE_PERCENTAGE) + 100);
	IsrCallsPerSec = GetIsrCalls(pAC);
	if (IsrCallsPerSec >= ThresholdInts) {
	    /*
	    ** We do not know how much the real CPU-load is!
	    ** Return 80% as a default in order to activate DIM
	    */
	    SystemLoad = 80;
	    return (SystemLoad);  
	} 

	UsedTime  = UserTime + NiceTime + SystemTime;

	IdleTime  = jif * num_online_cpus() - UsedTime;
	TotalTime = UsedTime + IdleTime;

	SystemLoad = ( 100 * (UsedTime  - M_DIMINFO.PrevUsedTime) ) /
						(TotalTime - M_DIMINFO.PrevTotalTime);

	if (M_DIMINFO.DisplayStats) {
		printk("Current system load is: %u\n", SystemLoad);
	}

	M_DIMINFO.PrevTotalTime = TotalTime;
	M_DIMINFO.PrevUsedTime  = UsedTime;

	return (SystemLoad);
}

/*******************************************************************************
** Function     : GetIsrCalls
** Description  : Depending on the selected moderation mask, this function will
**                return the number of interrupts handled in the previous time-
**                frame. This evaluated number is based on the current number 
**                of interrupts stored in PNMI-context and the previous stored 
**                interrupts.
** Programmer   : Ralph Roesler
** Last Modified: 23-mar-03
** Returns      : int:   the number of interrupts being executed in the last
**                       timeframe
** Notes        : It makes only sense to call this function, when dynamic 
**                interrupt moderation is applied
*******************************************************************************/

static SK_U64
GetIsrCalls(SK_AC *pAC) {
    SK_U64   RxPort0IntDiff = 0;
    SK_U64   RxPort1IntDiff = 0;
    SK_U64   TxPort0IntDiff = 0;
    SK_U64   TxPort1IntDiff = 0;

    if (pAC->DynIrqModInfo.MaskIrqModeration == IRQ_MASK_TX_ONLY) {
        if (pAC->GIni.GIMacsFound == 2) {
            TxPort1IntDiff = pAC->Pnmi.Port[1].TxIntrCts - 
                             pAC->DynIrqModInfo.PrevPort1TxIntrCts;
        }
        TxPort0IntDiff = pAC->Pnmi.Port[0].TxIntrCts - 
                         pAC->DynIrqModInfo.PrevPort0TxIntrCts;
    } else if (pAC->DynIrqModInfo.MaskIrqModeration == IRQ_MASK_RX_ONLY) {
        if (pAC->GIni.GIMacsFound == 2) {
            RxPort1IntDiff = pAC->Pnmi.Port[1].RxIntrCts - 
                             pAC->DynIrqModInfo.PrevPort1RxIntrCts;
        }
        RxPort0IntDiff = pAC->Pnmi.Port[0].RxIntrCts - 
                         pAC->DynIrqModInfo.PrevPort0RxIntrCts;
    } else {
        if (pAC->GIni.GIMacsFound == 2) {
            RxPort1IntDiff = pAC->Pnmi.Port[1].RxIntrCts - 
                             pAC->DynIrqModInfo.PrevPort1RxIntrCts;
            TxPort1IntDiff = pAC->Pnmi.Port[1].TxIntrCts - 
                             pAC->DynIrqModInfo.PrevPort1TxIntrCts;
        } 
        RxPort0IntDiff = pAC->Pnmi.Port[0].RxIntrCts - 
                         pAC->DynIrqModInfo.PrevPort0RxIntrCts;
        TxPort0IntDiff = pAC->Pnmi.Port[0].TxIntrCts - 
                         pAC->DynIrqModInfo.PrevPort0TxIntrCts;
    }

    return (RxPort0IntDiff + RxPort1IntDiff + TxPort0IntDiff + TxPort1IntDiff);
}

/*******************************************************************************
** Function     : GetRxCalls
** Description  : This function will return the number of times a receive inter-
**                rupt was processed. This is needed to evaluate any resizing 
**                factor.
** Programmer   : Ralph Roesler
** Last Modified: 23-mar-03
** Returns      : SK_U64: the number of RX-ints being processed
** Notes        : It makes only sense to call this function, when dynamic 
**                interrupt moderation is applied
*******************************************************************************/

static SK_U64
GetRxCalls(SK_AC *pAC) {
    SK_U64   RxPort0IntDiff = 0;
    SK_U64   RxPort1IntDiff = 0;

    if (pAC->GIni.GIMacsFound == 2) {
        RxPort1IntDiff = pAC->Pnmi.Port[1].RxIntrCts - 
                         pAC->DynIrqModInfo.PrevPort1RxIntrCts;
    }
    RxPort0IntDiff = pAC->Pnmi.Port[0].RxIntrCts - 
                     pAC->DynIrqModInfo.PrevPort0RxIntrCts;

    return (RxPort0IntDiff + RxPort1IntDiff);
}

/*******************************************************************************
** Function     : SetCurrIntCtr
** Description  : Will store the current number orf occured interrupts in the 
**                adapter context. This is needed to evaluated the number of 
**                interrupts within a current timeframe.
** Programmer   : Ralph Roesler
** Last Modified: 23-mar-03
** Returns      : void (!)
** Notes        : -
*******************************************************************************/

static void
SetCurrIntCtr(SK_AC *pAC) {
    if (pAC->GIni.GIMacsFound == 2) {
        pAC->DynIrqModInfo.PrevPort1RxIntrCts = pAC->Pnmi.Port[1].RxIntrCts;
        pAC->DynIrqModInfo.PrevPort1TxIntrCts = pAC->Pnmi.Port[1].TxIntrCts;
    } 
    pAC->DynIrqModInfo.PrevPort0RxIntrCts = pAC->Pnmi.Port[0].RxIntrCts;
    pAC->DynIrqModInfo.PrevPort0TxIntrCts = pAC->Pnmi.Port[0].TxIntrCts;
}

/*******************************************************************************
** Function     : IsIntModEnabled()
** Description  : Retrieves the current value of the interrupts moderation
**                command register. Its content determines whether any 
**                moderation is running or not.
** Programmer   : Ralph Roesler
** Last Modified: 23-mar-03
** Returns      : SK_TRUE  : if mod timer running
**                SK_FALSE : if no moderation is being performed
** Notes        : -
*******************************************************************************/

static SK_BOOL
IsIntModEnabled(SK_AC *pAC) {
    unsigned long CtrCmd;

    SK_IN32(pAC->IoBase, B2_IRQM_CTRL, &CtrCmd);
    if ((CtrCmd & TIM_START) == TIM_START) {
       return SK_TRUE;
    } else {
       return SK_FALSE;
    }
}

/*******************************************************************************
** Function     : EnableIntMod()
** Description  : Enables the interrupt moderation using the values stored in
**                in the pAC->DynIntMod data structure
** Programmer   : Ralph Roesler
** Last Modified: 22-mar-03
** Returns      : -
** Notes        : -
*******************************************************************************/

static void
EnableIntMod(SK_AC *pAC) {
    unsigned long ModBase;

    if (pAC->GIni.GIChipId == CHIP_ID_GENESIS) {
       ModBase = C_CLK_FREQ_GENESIS / pAC->DynIrqModInfo.MaxModIntsPerSec;
    } else {
       ModBase = C_CLK_FREQ_YUKON / pAC->DynIrqModInfo.MaxModIntsPerSec;
    }

    SK_OUT32(pAC->IoBase, B2_IRQM_INI,  ModBase);
    SK_OUT32(pAC->IoBase, B2_IRQM_MSK,  pAC->DynIrqModInfo.MaskIrqModeration);
    SK_OUT32(pAC->IoBase, B2_IRQM_CTRL, TIM_START);
    if (M_DIMINFO.DisplayStats) {
        printk("Enabled interrupt moderation (%i ints/sec)\n",
               M_DIMINFO.MaxModIntsPerSec);
    }
}

/*******************************************************************************
** Function     : DisableIntMod()
** Description  : Disbles the interrupt moderation independent of what inter-
**                rupts are running or not
** Programmer   : Ralph Roesler
** Last Modified: 23-mar-03
** Returns      : -
** Notes        : -
*******************************************************************************/

static void 
DisableIntMod(SK_AC *pAC) {

    SK_OUT32(pAC->IoBase, B2_IRQM_CTRL, TIM_STOP);
    if (M_DIMINFO.DisplayStats) {
        printk("Disabled interrupt moderation\n");
    }
} 

/*******************************************************************************
** Function     : ResizeDimTimerDuration();
** Description  : Checks the current used descriptor ratio and resizes the 
**                duration timer (longer/smaller) if possible. 
** Programmer   : Ralph Roesler
** Last Modified: 23-mar-03
** Returns      : -
** Notes        : There are both maximum and minimum timer duration value. 
**                This function assumes that interrupt moderation is already
**                enabled!
*******************************************************************************/

static void 
ResizeDimTimerDuration(SK_AC *pAC) {
    SK_BOOL IncreaseTimerDuration;
    int     TotalMaxNbrDescr;
    int     UsedDescrRatio;
    int     RatioDiffAbs;
    int     RatioDiffRel;
    int     NewMaxModIntsPerSec;
    int     ModAdjValue;
    long    ModBase;

    /*
    ** Check first if we are allowed to perform any modification
    */
    if (IsIntModEnabled(pAC)) { 
        if (M_DIMINFO.IntModTypeSelect != C_INT_MOD_DYNAMIC) {
            return; 
        } else {
            if (M_DIMINFO.ModJustEnabled) {
                M_DIMINFO.ModJustEnabled = SK_FALSE;
                return;
            }
        }
    }

    /*
    ** If we got until here, we have to evaluate the amount of the
    ** descriptor ratio change...
    */
    TotalMaxNbrDescr = pAC->RxDescrPerRing * GetRxCalls(pAC);
    UsedDescrRatio   = (M_DIMINFO.NbrProcessedDescr * 100) / TotalMaxNbrDescr;

    if (UsedDescrRatio > M_DIMINFO.PrevUsedDescrRatio) {
        RatioDiffAbs = (UsedDescrRatio - M_DIMINFO.PrevUsedDescrRatio);
        RatioDiffRel = (RatioDiffAbs * 100) / UsedDescrRatio;
        M_DIMINFO.PrevUsedDescrRatio = UsedDescrRatio;
        IncreaseTimerDuration = SK_FALSE;  /* in other words: DECREASE */
    } else if (UsedDescrRatio < M_DIMINFO.PrevUsedDescrRatio) {
        RatioDiffAbs = (M_DIMINFO.PrevUsedDescrRatio - UsedDescrRatio);
        RatioDiffRel = (RatioDiffAbs * 100) / M_DIMINFO.PrevUsedDescrRatio;
        M_DIMINFO.PrevUsedDescrRatio = UsedDescrRatio;
        IncreaseTimerDuration = SK_TRUE;   /* in other words: INCREASE */
    } else {
        RatioDiffAbs = (M_DIMINFO.PrevUsedDescrRatio - UsedDescrRatio);
        RatioDiffRel = (RatioDiffAbs * 100) / M_DIMINFO.PrevUsedDescrRatio;
        M_DIMINFO.PrevUsedDescrRatio = UsedDescrRatio;
        IncreaseTimerDuration = SK_TRUE;   /* in other words: INCREASE */
    }

    /*
    ** Now we can determine the change in percent
    */
    if ((RatioDiffRel >= 0) && (RatioDiffRel <= 5) ) {
       ModAdjValue = 1;  /*  1% change - maybe some other value in future */
    } else if ((RatioDiffRel > 5) && (RatioDiffRel <= 10) ) {
       ModAdjValue = 1;  /*  1% change - maybe some other value in future */
    } else if ((RatioDiffRel > 10) && (RatioDiffRel <= 15) ) {
       ModAdjValue = 1;  /*  1% change - maybe some other value in future */
    } else {
       ModAdjValue = 1;  /*  1% change - maybe some other value in future */
    }

    if (IncreaseTimerDuration) {
       NewMaxModIntsPerSec =  M_DIMINFO.MaxModIntsPerSec +
                             (M_DIMINFO.MaxModIntsPerSec * ModAdjValue) / 100;
    } else {
       NewMaxModIntsPerSec =  M_DIMINFO.MaxModIntsPerSec -
                             (M_DIMINFO.MaxModIntsPerSec * ModAdjValue) / 100;
    }

    /* 
    ** Check if we exceed boundaries...
    */
    if ( (NewMaxModIntsPerSec > M_DIMINFO.MaxModIntsPerSecUpperLimit) ||
         (NewMaxModIntsPerSec < M_DIMINFO.MaxModIntsPerSecLowerLimit)) {
        if (M_DIMINFO.DisplayStats) {
            printk("Cannot change ModTim from %i to %i ints/sec\n",
                   M_DIMINFO.MaxModIntsPerSec, NewMaxModIntsPerSec);
        }
        return;
    } else {
        if (M_DIMINFO.DisplayStats) {
            printk("Resized ModTim from %i to %i ints/sec\n",
                   M_DIMINFO.MaxModIntsPerSec, NewMaxModIntsPerSec);
        }
    }

    M_DIMINFO.MaxModIntsPerSec = NewMaxModIntsPerSec;

    if (pAC->GIni.GIChipId == CHIP_ID_GENESIS) {
        ModBase = C_CLK_FREQ_GENESIS / pAC->DynIrqModInfo.MaxModIntsPerSec;
    } else {
        ModBase = C_CLK_FREQ_YUKON / pAC->DynIrqModInfo.MaxModIntsPerSec;
    }

    /* 
    ** We do not need to touch any other registers
    */
    SK_OUT32(pAC->IoBase, B2_IRQM_INI, ModBase);
} 

/*******************************************************************************
** Function     : DisplaySelectedModerationType()
** Description  : Displays what type of moderation we have
** Programmer   : Ralph Roesler
** Last Modified: 23-mar-03
** Returns      : void!
** Notes        : -
*******************************************************************************/

static void
DisplaySelectedModerationType(SK_AC *pAC) {

    if (pAC->DynIrqModInfo.DisplayStats) {
        if (pAC->DynIrqModInfo.IntModTypeSelect == C_INT_MOD_STATIC) {
             printk("Static int moderation runs with %i INTS/sec\n",
                    pAC->DynIrqModInfo.MaxModIntsPerSec);
        } else if (pAC->DynIrqModInfo.IntModTypeSelect == C_INT_MOD_DYNAMIC) {
             if (IsIntModEnabled(pAC)) {
                printk("Dynamic int moderation runs with %i INTS/sec\n",
                       pAC->DynIrqModInfo.MaxModIntsPerSec);
             } else {
                printk("Dynamic int moderation currently not applied\n");
             }
        } else {
             printk("No interrupt moderation selected!\n");
        }
    }
}

/*******************************************************************************
** Function     : DisplaySelectedModerationMask()
** Description  : Displays what interrupts are moderated
** Programmer   : Ralph Roesler
** Last Modified: 23-mar-03
** Returns      : void!
** Notes        : -
*******************************************************************************/

static void
DisplaySelectedModerationMask(SK_AC *pAC) {

    if (pAC->DynIrqModInfo.DisplayStats) {
        if (pAC->DynIrqModInfo.IntModTypeSelect != C_INT_MOD_NONE) {
            switch (pAC->DynIrqModInfo.MaskIrqModeration) {
                case IRQ_MASK_TX_ONLY: 
                   printk("Only Tx-interrupts are moderated\n");
                   break;
                case IRQ_MASK_RX_ONLY: 
                   printk("Only Rx-interrupts are moderated\n");
                   break;
                case IRQ_MASK_SP_ONLY: 
                   printk("Only special-interrupts are moderated\n");
                   break;
                case IRQ_MASK_TX_RX: 
                   printk("Tx- and Rx-interrupts are moderated\n");
                   break;
                case IRQ_MASK_SP_RX: 
                   printk("Special- and Rx-interrupts are moderated\n");
                   break;
                case IRQ_MASK_SP_TX: 
                   printk("Special- and Tx-interrupts are moderated\n");
                   break;
                case IRQ_MASK_RX_TX_SP:
                   printk("All Rx-, Tx and special-interrupts are moderated\n");
                   break;
                default:
                   printk("Don't know what is moderated\n");
                   break;
            }
        } else {
            printk("No specific interrupts masked for moderation\n");
        }
    } 
}

/*******************************************************************************
** Function     : DisplayDescrRatio
** Description  : Like the name states...
** Programmer   : Ralph Roesler
** Last Modified: 23-mar-03
** Returns      : void!
** Notes        : -
*******************************************************************************/

static void
DisplayDescrRatio(SK_AC *pAC) {
    int TotalMaxNbrDescr = 0;

    if (pAC->DynIrqModInfo.DisplayStats) {
        TotalMaxNbrDescr = pAC->RxDescrPerRing * GetRxCalls(pAC);
        printk("Ratio descriptors: %i/%i\n",
               M_DIMINFO.NbrProcessedDescr, TotalMaxNbrDescr);
    }
}

/*******************************************************************************
**
** End of file
**
*******************************************************************************/