root/usr/src/uts/common/io/bnx/bnxtmr.c
/*
 * Copyright 2014-2017 Cavium, Inc.
 * The contents of this file are subject to the terms of the Common Development
 * and Distribution License, v.1,  (the "License").
 *
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the License at available
 * at http://opensource.org/licenses/CDDL-1.0
 *
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
 * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
 * Copyright (c) 2019, Joyent, Inc.
 */

#include "bnxtmr.h"
#include "bnxrcv.h"
#include "bnxgld.h"


/* 1.5 seconds */
#define BNX_LINK_CHECK_INTERVAL 10

/* Approximately every second. */
#define BNX_LINK_CHECK_INTERVAL2 7

/* 500 msecs */
#define BNX_TIMER_INTERVAL      500000


typedef struct _bnx_fw_t {
        u32_t shmemaddr;
        u32_t length;
        u32_t nvramaddr;
} bnx_fw_t;

static void
bnx_link_check(lm_device_t *const lmdevice)
{
        if (lmdevice->vars.link_status == LM_STATUS_LINK_ACTIVE) {
                /*
                 * If we have link and we are in the fallback (1gb forced),
                 * mode, we need to see if our link partner is sending us
                 * configs.  If this is the case, we'll switch back to autoneg.
                 */
                if (lmdevice->vars.serdes_fallback_status) {
                        u32_t intr_exp_status;

                        (void) lm_mwrite(lmdevice, lmdevice->params.phy_addr,
                            0x17, 0x0f01);
                        (void) lm_mread(lmdevice, lmdevice->params.phy_addr,
                            0x15, &intr_exp_status);
                        (void) lm_mread(lmdevice, lmdevice->params.phy_addr,
                            0x15, &intr_exp_status);

                        if (intr_exp_status & 0x20) {
                                (void) lm_mwrite(lmdevice,
                                    lmdevice->params.phy_addr,
                                    PHY_CTRL_REG, PHY_CTRL_AUTO_NEG_ENABLE |
                                    PHY_CTRL_RESTART_AUTO_NEG);
                        }
                }
        } else {
                lm_service_phy_int(lmdevice, TRUE);
        }
}

static void
bnx_link_check2(lm_device_t *const lmdevice)
{
        if (lmdevice->vars.link_status == LM_STATUS_LINK_ACTIVE) {
                u32_t val;
                u32_t phy_addr;

                phy_addr = lmdevice->params.phy_addr;

                /* Is the link really up? */
                (void) lm_mwrite(lmdevice, phy_addr, 0x1c, 0x6800);
                (void) lm_mread(lmdevice, phy_addr, 0x1c, &val);
                (void) lm_mread(lmdevice, phy_addr, 0x1c, &val);

                if (val & 2) {
                        /* Nope.  Force the link down. */
                        (void) lm_mwrite(lmdevice, phy_addr, 0x17, 0x0f03);
                        (void) lm_mread(lmdevice, phy_addr, 0x15, &val);
                        (void) lm_mwrite(lmdevice, phy_addr, 0x15,
                            val & 0xff0f);

                        lmdevice->vars.bcm5706s_tx_drv_cur = (u16_t)val;
                }
        }
}



/*
 * Name:    bnx_timer
 *
 * Input:   ptr to device structure
 *
 * Return:  None
 *
 * Description: bnx_timer is the periodic timer callback funtion.
 */
static void
bnx_timer(void *arg)
{
        lm_device_t *lmdevice;
        um_device_t *umdevice;

        umdevice = (um_device_t *)arg;
        lmdevice = &(umdevice->lm_dev);

        mutex_enter(&umdevice->tmr_mutex);

        if (umdevice->timer_enabled != B_TRUE) {
                goto done;
        }

        um_send_driver_pulse(umdevice);

        /*
         * Take this opportunity to replenish any unused Rx Bds.  Don't
         * wait around for the rcv_mutex though.  We share the
         * responsibility of replenishing the rx buffers with the ISR.
         */
        if (mutex_tryenter(&umdevice->os_param.rcv_mutex)) {
                /* This function does an implicit *_fill(). */
                bnx_rxpkts_post(umdevice);

                mutex_exit(&umdevice->os_param.rcv_mutex);
        }

        if (umdevice->timer_link_check_interval2) {
                /*
                 * If enabled, check to see if the serdes
                 * PHY can fallback to a forced mode.
                 */
                if (umdevice->timer_link_check_interval) {
                        if (umdevice->timer_link_check_counter) {
                                if (umdevice->timer_link_check_counter == 1) {
                                        mutex_enter(
                                            &umdevice->os_param.phy_mutex);
                                        bnx_link_check(lmdevice);
                                        mutex_exit(
                                            &umdevice->os_param.phy_mutex);
                                }
                                umdevice->timer_link_check_counter--;
                        }
                }

                umdevice->timer_link_check_counter2--;
                if (umdevice->timer_link_check_counter2 == 0) {
                        mutex_enter(&umdevice->os_param.phy_mutex);
                        bnx_link_check2(lmdevice);
                        mutex_exit(&umdevice->os_param.phy_mutex);

                        umdevice->timer_link_check_counter2 =
                            umdevice->timer_link_check_interval2;
                }
        }

        FLUSHPOSTEDWRITES(lmdevice);

        umdevice->tmrtid = timeout(bnx_timer, (void *)umdevice,
            drv_usectohz(BNX_TIMER_INTERVAL));

done:
        mutex_exit(&umdevice->tmr_mutex);
}

void
bnx_timer_start(um_device_t *const umdevice)
{
        lm_device_t *lmdevice;

        lmdevice = &(umdevice->lm_dev);

        umdevice->timer_enabled = B_TRUE;

        if (CHIP_NUM(lmdevice) == CHIP_NUM_5706 &&
            umdevice->dev_var.isfiber == B_TRUE) {
                if (lmdevice->vars.serdes_fallback_select !=
                    SERDES_FALLBACK_NONE) {
                        umdevice->timer_link_check_interval =
                            BNX_LINK_CHECK_INTERVAL;
                } else {
                        umdevice->timer_link_check_interval = 0;
                }

                umdevice->timer_link_check_interval2 = BNX_LINK_CHECK_INTERVAL2;
                umdevice->timer_link_check_counter2 =
                    umdevice->timer_link_check_interval2;
        } else {
                umdevice->timer_link_check_interval2 = 0;
        }

        umdevice->tmrtid = timeout(bnx_timer, (void *)umdevice,
            drv_usectohz(BNX_TIMER_INTERVAL));
}


void
bnx_timer_stop(um_device_t *const umdevice)
{
        mutex_enter(&umdevice->tmr_mutex);
        umdevice->timer_enabled = B_FALSE;
        mutex_exit(&umdevice->tmr_mutex);

        (void) untimeout(umdevice->tmrtid);
        umdevice->tmrtid = 0;
}



/*
 * Name:        bnx_link_timer_restart
 *
 * Input:       ptr to device structure
 *
 * Return:      None
 *
 * Description: This function restarts the link poll timer
 *
 */
void
bnx_link_timer_restart(um_device_t *const umdevice)
{
        /* FIXME -- Make timer_link_check_counter atomic */
        umdevice->timer_link_check_counter =
            umdevice->timer_link_check_interval;
}



void
bnx_timer_init(um_device_t *const umdevice)
{
        mutex_init(&umdevice->tmr_mutex, NULL, MUTEX_DRIVER,
            DDI_INTR_PRI(umdevice->intrPriority));
}



void
bnx_timer_fini(um_device_t *const umdevice)
{
        mutex_destroy(&umdevice->tmr_mutex);
}