root/usr/src/uts/common/io/bnxe/577xx/drivers/common/lm/fw/bnxe_fw_funcs.c




#include "lm5710.h"
#include "init_defs.h"

/* Vnics per mode */
#define ECORE_PORT2_MODE_NUM_VNICS 4


/* QM queue numbers */
#define ECORE_ETH_Q             0
#define ECORE_TOE_Q             3
#define ECORE_TOE_ACK_Q         6
#define ECORE_ISCSI_Q           9
#define ECORE_ISCSI_ACK_Q       11
#define ECORE_FCOE_Q            10

/* Vnics per mode */
#define ECORE_PORT4_MODE_NUM_VNICS 2

/* COS offset for port1 in E3 B0 4port mode */
#define ECORE_E3B0_PORT1_COS_OFFSET 3

/* QM Register addresses */
#define ECORE_Q_VOQ_REG_ADDR(pf_q_num)\
        (QM_REG_QVOQIDX_0 + 4 * (pf_q_num))
#define ECORE_VOQ_Q_REG_ADDR(cos, pf_q_num)\
        (QM_REG_VOQQMASK_0_LSB + 4 * ((cos) * 2 + ((pf_q_num) >> 5)))
#define ECORE_Q_CMDQ_REG_ADDR(pf_q_num)\
        (QM_REG_BYTECRDCMDQ_0 + 4 * ((pf_q_num) >> 4))

/* extracts the QM queue number for the specified port and vnic */
#define ECORE_PF_Q_NUM(q_num, port, vnic)\
        ((((port) << 1) | (vnic)) * 16 + (q_num))


/* Maps the specified queue to the specified COS */
void ecore_map_q_cos(struct _lm_device_t *pdev, u32_t q_num, u32_t new_cos)
{
        /* find current COS mapping */
        u32_t curr_cos = REG_RD(pdev, QM_REG_QVOQIDX_0 + q_num * 4);

        /* check if queue->COS mapping has changed */
        if (curr_cos != new_cos) {
                u32_t num_vnics = ECORE_PORT2_MODE_NUM_VNICS;
                u32_t reg_addr, reg_bit_map, vnic;

                /* update parameters for 4port mode */
                if (INIT_MODE_FLAGS(pdev) & MODE_PORT4) {
                        num_vnics = ECORE_PORT4_MODE_NUM_VNICS;
                        if (PORT_ID(pdev)) {
                                curr_cos += ECORE_E3B0_PORT1_COS_OFFSET;
                                new_cos += ECORE_E3B0_PORT1_COS_OFFSET;
                        }
                }

                /* change queue mapping for each VNIC */
                for (vnic = 0; vnic < num_vnics; vnic++) {
                        u32_t pf_q_num =
                                ECORE_PF_Q_NUM(q_num, PORT_ID(pdev), vnic);
                        u32_t q_bit_map = 1 << (pf_q_num & 0x1f);

                        /* overwrite queue->VOQ mapping */
                        REG_WR(pdev, ECORE_Q_VOQ_REG_ADDR(pf_q_num), new_cos);

                        /* clear queue bit from current COS bit map */
                        reg_addr = ECORE_VOQ_Q_REG_ADDR(curr_cos, pf_q_num);
                        reg_bit_map = REG_RD(pdev, reg_addr);
                        REG_WR(pdev, reg_addr, reg_bit_map & (~q_bit_map));

                        /* set queue bit in new COS bit map */
                        reg_addr = ECORE_VOQ_Q_REG_ADDR(new_cos, pf_q_num);
                        reg_bit_map = REG_RD(pdev, reg_addr);
                        REG_WR(pdev, reg_addr, reg_bit_map | q_bit_map);

                        /* set/clear queue bit in command-queue bit map
                        (E2/E3A0 only, valid COS values are 0/1) */
                        if (!(INIT_MODE_FLAGS(pdev) & MODE_E3_B0)) {
                                reg_addr = ECORE_Q_CMDQ_REG_ADDR(pf_q_num);
                                reg_bit_map = REG_RD(pdev, reg_addr);
                                q_bit_map = 1 << (2 * (pf_q_num & 0xf));
                                reg_bit_map = new_cos ?
                                              (reg_bit_map | q_bit_map) :
                                              (reg_bit_map & (~q_bit_map));
                                REG_WR(pdev, reg_addr, reg_bit_map);
                        }
                }
        }
}

/* Configures the QM according to the specified per-traffic-type COSes */
void ecore_dcb_config_qm(struct _lm_device_t *pdev, enum cos_mode mode,
                                       struct priority_cos *traffic_cos)
{
        ecore_map_q_cos(pdev, ECORE_FCOE_Q,
                        traffic_cos[LLFC_TRAFFIC_TYPE_FCOE].cos);
        ecore_map_q_cos(pdev, ECORE_ISCSI_Q,
                        traffic_cos[LLFC_TRAFFIC_TYPE_ISCSI].cos);
        ecore_map_q_cos(pdev, ECORE_ISCSI_ACK_Q,
                traffic_cos[LLFC_TRAFFIC_TYPE_ISCSI].cos);
        if (mode != STATIC_COS) {
                /* required only in OVERRIDE_COS mode */
                ecore_map_q_cos(pdev, ECORE_ETH_Q,
                                traffic_cos[LLFC_TRAFFIC_TYPE_NW].cos);
                ecore_map_q_cos(pdev, ECORE_TOE_Q,
                                traffic_cos[LLFC_TRAFFIC_TYPE_NW].cos);
                ecore_map_q_cos(pdev, ECORE_TOE_ACK_Q,
                                traffic_cos[LLFC_TRAFFIC_TYPE_NW].cos);
        }
}


/*
 * congestion managment port init api description
 * the api works as follows:
 * the driver should pass the cmng_init_input struct, the port_init function
 * will prepare the required internal ram structure which will be passed back
 * to the driver (cmng_init) that will write it into the internal ram.
 *
 * IMPORTANT REMARKS:
 * 1. the cmng_init struct does not represent the contiguous internal ram
 *    structure. the driver should use the XSTORM_CMNG_PERPORT_VARS_OFFSET
 *    offset in order to write the port sub struct and the
 *    PFID_FROM_PORT_AND_VNIC offset for writing the vnic sub struct (in other
 *    words - don't use memcpy!).
 * 2. although the cmng_init struct is filled for the maximal vnic number
 *    possible, the driver should only write the valid vnics into the internal
 *    ram according to the appropriate port mode.
 */
#define BITS_TO_BYTES(x) ((x)/8)

/* CMNG constants, as derived from system spec calculations */

/* default MIN rate in case VNIC min rate is configured to zero- 100Mbps */
#define DEF_MIN_RATE 100

/* resolution of the rate shaping timer - 400 usec */
#define RS_PERIODIC_TIMEOUT_USEC 400

/*
 *  number of bytes in single QM arbitration cycle -
 *  coefficient for calculating the fairness timer
 */
#define QM_ARB_BYTES 160000

/* resolution of Min algorithm 1:100 */
#define MIN_RES 100

/*
 *  how many bytes above threshold for
 *  the minimal credit of Min algorithm
 */
#define MIN_ABOVE_THRESH 32768

/*
 *  Fairness algorithm integration time coefficient -
 *  for calculating the actual Tfair
 */
#define T_FAIR_COEF ((MIN_ABOVE_THRESH + QM_ARB_BYTES) * 8 * MIN_RES)

/* Memory of fairness algorithm - 2 cycles */
#define FAIR_MEM 2
#define SAFC_TIMEOUT_USEC 52

#define SDM_TICKS 4


void ecore_init_max(const struct cmng_init_input *input_data,
                                  u32_t r_param, struct cmng_init *ram_data)
{
        u32_t vnic;
        struct cmng_vnic *vdata = &ram_data->vnic;
        struct cmng_struct_per_port *pdata = &ram_data->port;
        /*
         * rate shaping per-port variables
         *  100 micro seconds in SDM ticks = 25
         *  since each tick is 4 microSeconds
         */

        pdata->rs_vars.rs_periodic_timeout =
        RS_PERIODIC_TIMEOUT_USEC / SDM_TICKS;

        /* this is the threshold below which no timer arming will occur.
         *  1.25 coefficient is for the threshold to be a little bigger
         *  then the real time to compensate for timer in-accuracy
         */
        pdata->rs_vars.rs_threshold =
        (5 * RS_PERIODIC_TIMEOUT_USEC * r_param)/4;

        /* rate shaping per-vnic variables */
        for (vnic = 0; vnic < ECORE_PORT2_MODE_NUM_VNICS; vnic++) {
                /* global vnic counter */
                vdata->vnic_max_rate[vnic].vn_counter.rate =
                input_data->vnic_max_rate[vnic];
                /*
                 * maximal Mbps for this vnic
                 * the quota in each timer period - number of bytes
                 * transmitted in this period
                 */
                vdata->vnic_max_rate[vnic].vn_counter.quota =
                        RS_PERIODIC_TIMEOUT_USEC *
                        (u32_t)vdata->vnic_max_rate[vnic].vn_counter.rate / 8;
        }

}

void ecore_init_max_per_vn(u16_t vnic_max_rate,
                                  struct rate_shaping_vars_per_vn *ram_data)
{       
        /* global vnic counter */
        ram_data->vn_counter.rate = vnic_max_rate;
        
        /*
        * maximal Mbps for this vnic
        * the quota in each timer period - number of bytes
        * transmitted in this period
        */
        ram_data->vn_counter.quota = 
                RS_PERIODIC_TIMEOUT_USEC * (u32_t)vnic_max_rate / 8;
}

void ecore_init_min(const struct cmng_init_input *input_data,
                                  u32_t r_param, struct cmng_init *ram_data)
{
        u32_t vnic, fair_periodic_timeout_usec, vnicWeightSum, tFair;
        struct cmng_vnic *vdata = &ram_data->vnic;
        struct cmng_struct_per_port *pdata = &ram_data->port;

        /* this is the resolution of the fairness timer */
        fair_periodic_timeout_usec = QM_ARB_BYTES / r_param;

        /*
         * fairness per-port variables
         * for 10G it is 1000usec. for 1G it is 10000usec.
         */
        tFair = T_FAIR_COEF / input_data->port_rate;

        /* this is the threshold below which we won't arm the timer anymore */
        pdata->fair_vars.fair_threshold = QM_ARB_BYTES;

        /*
         *  we multiply by 1e3/8 to get bytes/msec. We don't want the credits
         *  to pass a credit of the T_FAIR*FAIR_MEM (algorithm resolution)
         */
        pdata->fair_vars.upper_bound = r_param * tFair * FAIR_MEM;

        /* since each tick is 4 microSeconds */
        pdata->fair_vars.fairness_timeout =
                                fair_periodic_timeout_usec / SDM_TICKS;

        /* calculate sum of weights */
        vnicWeightSum = 0;

        for (vnic = 0; vnic < ECORE_PORT2_MODE_NUM_VNICS; vnic++)
                vnicWeightSum += input_data->vnic_min_rate[vnic];

        /* global vnic counter */
        if (vnicWeightSum > 0) {
                /* fairness per-vnic variables */
                for (vnic = 0; vnic < ECORE_PORT2_MODE_NUM_VNICS; vnic++) {
                        /*
                         *  this is the credit for each period of the fairness
                         *  algorithm - number of bytes in T_FAIR (this vnic
                         *  share of the port rate)
                         */
                        vdata->vnic_min_rate[vnic].vn_credit_delta =
                                ((u32_t)(input_data->vnic_min_rate[vnic]) * 100 *
                                (T_FAIR_COEF / (8 * 100 * vnicWeightSum)));
                        if (vdata->vnic_min_rate[vnic].vn_credit_delta <
                            pdata->fair_vars.fair_threshold +
                            MIN_ABOVE_THRESH) {
                                vdata->vnic_min_rate[vnic].vn_credit_delta =
                                        pdata->fair_vars.fair_threshold +
                                        MIN_ABOVE_THRESH;
                        }
                }
        }
}

void ecore_init_fw_wrr(const struct cmng_init_input *input_data,
                                     u32_t r_param, struct cmng_init *ram_data)
{
        u32_t vnic, cos;
        u32_t cosWeightSum = 0;
        struct cmng_vnic *vdata = &ram_data->vnic;
        struct cmng_struct_per_port *pdata = &ram_data->port;

        for (cos = 0; cos < MAX_COS_NUMBER; cos++)
                cosWeightSum += input_data->cos_min_rate[cos];

        if (cosWeightSum > 0) {

                for (vnic = 0; vnic < ECORE_PORT2_MODE_NUM_VNICS; vnic++) {
                        /*
                         *  Since cos and vnic shouldn't work together the rate
                         *  to divide between the coses is the port rate.
                         */
                        u32_t *ccd = vdata->vnic_min_rate[vnic].cos_credit_delta;
                        for (cos = 0; cos < MAX_COS_NUMBER; cos++) {
                                /*
                                 * this is the credit for each period of
                                 * the fairness algorithm - number of bytes
                                 * in T_FAIR (this cos share of the vnic rate)
                                 */
                                ccd[cos] =
                                    ((u32_t)input_data->cos_min_rate[cos] * 100 *
                                    (T_FAIR_COEF / (8 * 100 * cosWeightSum)));
                                 if (ccd[cos] < pdata->fair_vars.fair_threshold
                                                + MIN_ABOVE_THRESH) {
                                        ccd[cos] =
                                            pdata->fair_vars.fair_threshold +
                                            MIN_ABOVE_THRESH;
                                }
                        }
                }
        }
}

void ecore_init_safc(const struct cmng_init_input *input_data,
                                   struct cmng_init *ram_data)
{
        /* in microSeconds */
        ram_data->port.safc_vars.safc_timeout_usec = SAFC_TIMEOUT_USEC;
}

/* Congestion management port init */
void ecore_init_cmng(const struct cmng_init_input *input_data,
                                   struct cmng_init *ram_data)
{
        u32_t r_param;
        mm_mem_zero(ram_data,sizeof(struct cmng_init));

        ram_data->port.flags = input_data->flags;

        /*
         *  number of bytes transmitted in a rate of 10Gbps
         *  in one usec = 1.25KB.
         */
        r_param = BITS_TO_BYTES(input_data->port_rate);
        ecore_init_max(input_data, r_param, ram_data);
        ecore_init_min(input_data, r_param, ram_data);
        ecore_init_fw_wrr(input_data, r_param, ram_data);
        ecore_init_safc(input_data, ram_data);
}