root/usr/src/uts/common/io/ena/ena_hw.c
/*
 * This file and its contents are supplied under the terms of the
 * Common Development and Distribution License ("CDDL"), version 1.0.
 * You may only use this file in accordance with the terms of version
 * 1.0 of the CDDL.
 *
 * A full copy of the text of the CDDL should have accompanied this
 * source.  A copy of the CDDL is also available via the Internet at
 * http://www.illumos.org/license/CDDL.
 */

/*
 * Copyright 2024 Oxide Computer Company
 */

#include "ena_hw.h"
#include "ena.h"

uint32_t
ena_hw_bar_read32(const ena_t *ena, const uint16_t offset)
{
        caddr_t addr = ena->ena_reg_base + offset;
        return (ena_hw_abs_read32(ena, (uint32_t *)addr));
}

uint32_t
ena_hw_abs_read32(const ena_t *ena, uint32_t *addr)
{
        VERIFY3U(addr, >=, ena->ena_reg_base);
        VERIFY3U(addr, <, ena->ena_reg_base + (ena->ena_reg_size - 4));

        return (ddi_get32(ena->ena_reg_hdl, addr));
}

void
ena_hw_bar_write32(const ena_t *ena, const uint16_t offset, const uint32_t val)
{
        caddr_t addr = ena->ena_reg_base + offset;
        ena_hw_abs_write32(ena, (uint32_t *)addr, val);
}

void
ena_hw_abs_write32(const ena_t *ena, uint32_t *addr, const uint32_t val)
{
        VERIFY3P(ena, !=, NULL);
        VERIFY3P(addr, !=, NULL);
        VERIFY3U(addr, >=, ena->ena_reg_base);
        VERIFY3U(addr, <, ena->ena_reg_base + (ena->ena_reg_size - 4));

        ddi_put32(ena->ena_reg_hdl, addr, val);
}

int
enahw_resp_status_to_errno(ena_t *ena, enahw_resp_status_t status)
{
        int ret = 0;

        switch (status) {
        case ENAHW_RESP_SUCCESS:
                break;

        case ENAHW_RESP_RESOURCE_ALLOCATION_FAILURE:
                ret = ENOMEM;
                break;

        case ENAHW_RESP_UNSUPPORTED_OPCODE:
                ret = ENOTSUP;
                break;

        case ENAHW_RESP_BAD_OPCODE:
        case ENAHW_RESP_MALFORMED_REQUEST:
        case ENAHW_RESP_ILLEGAL_PARAMETER:
                ret = EINVAL;
                break;

        case ENAHW_RESP_RESOURCE_BUSY:
                ret = EAGAIN;
                break;

        case ENAHW_RESP_UNKNOWN_ERROR:
        default:
                /*
                 * If the device presents us with an "unknown error"
                 * code, or the status code is undefined, then we log
                 * an error and convert it to EIO.
                 */
                ena_err(ena, "unexpected status code: %d", status);
                ret = EIO;
                break;
        }

        return (ret);
}

const char *
enahw_reset_reason(enahw_reset_reason_t reason)
{
        switch (reason) {
        case ENAHW_RESET_NORMAL:
                return ("normal");
        case ENAHW_RESET_KEEP_ALIVE_TO:
                return ("keep-alive timeout");
        case ENAHW_RESET_ADMIN_TO:
                return ("admin timeout");
        case ENAHW_RESET_MISS_TX_CMPL:
                return ("missed TX completion");
        case ENAHW_RESET_INV_RX_REQ_ID:
                return ("invalid RX request ID");
        case ENAHW_RESET_INV_TX_REQ_ID:
                return ("invalid TX request ID");
        case ENAHW_RESET_TOO_MANY_RX_DESCS:
                return ("too many RX descs");
        case ENAHW_RESET_INIT_ERR:
                return ("initialization error");
        case ENAHW_RESET_DRIVER_INVALID_STATE:
                return ("invalid driver state");
        case ENAHW_RESET_OS_TRIGGER:
                return ("OS trigger");
        case ENAHW_RESET_OS_NETDEV_WD:
                return ("netdev watchdog");
        case ENAHW_RESET_SHUTDOWN:
                return ("shutdown");
        case ENAHW_RESET_USER_TRIGGER:
                return ("user trigger");
        case ENAHW_RESET_GENERIC:
                return ("generic");
        case ENAHW_RESET_MISS_INTERRUPT:
                return ("missed interrupt");
        case ENAHW_RESET_SUSPECTED_POLL_STARVATION:
                return ("suspected poll starvation");
        case ENAHW_RESET_RX_DESCRIPTOR_MALFORMED:
                return ("malformed RX descriptor");
        case ENAHW_RESET_TX_DESCRIPTOR_MALFORMED:
                return ("malformed TX descriptor");
        case ENAHW_RESET_MISSING_ADMIN_INTERRUPT:
                return ("missing admin interrupt");
        case ENAHW_RESET_DEVICE_REQUEST:
                return ("device request");
        default:
                return ("unknown");
        }
}

#ifdef DEBUG
static const ena_reg_t reg_cache_template[ENAHW_NUM_REGS] = {
        {
                .er_name = "Version",
                .er_offset = ENAHW_REG_VERSION
        },
        {
                .er_name = "Controller Version",
                .er_offset = ENAHW_REG_CONTROLLER_VERSION
        },
        {
                .er_name = "Caps",
                .er_offset = ENAHW_REG_CAPS
        },
        {
                .er_name = "Extended Caps",
                .er_offset = ENAHW_REG_CAPS_EXT
        },
        {
                .er_name = "Admin SQ Base Low",
                .er_offset = ENAHW_REG_ASQ_BASE_LO
        },
        {
                .er_name = "Admin SQ Base High",
                .er_offset = ENAHW_REG_ASQ_BASE_HI
        },
        {
                .er_name = "Admin SQ Caps",
                .er_offset = ENAHW_REG_ASQ_CAPS
        },
        {
                .er_name = "Gap 0x1C",
                .er_offset = ENAHW_REG_GAP_1C
        },
        {
                .er_name = "Admin CQ Base Low",
                .er_offset = ENAHW_REG_ACQ_BASE_LO
        },
        {
                .er_name = "Admin CQ Base High",
                .er_offset = ENAHW_REG_ACQ_BASE_HI
        },
        {
                .er_name = "Admin CQ Caps",
                .er_offset = ENAHW_REG_ACQ_CAPS
        },
        {
                .er_name = "Admin SQ Doorbell",
                .er_offset = ENAHW_REG_ASQ_DB
        },
        {
                .er_name = "Admin CQ Tail",
                .er_offset = ENAHW_REG_ACQ_TAIL
        },
        {
                .er_name = "Admin Event Notification Queue Caps",
                .er_offset = ENAHW_REG_AENQ_CAPS
        },
        {
                .er_name = "Admin Event Notification Queue Base Low",
                .er_offset = ENAHW_REG_AENQ_BASE_LO
        },
        {
                .er_name = "Admin Event Notification Queue Base High",
                .er_offset = ENAHW_REG_AENQ_BASE_HI
        },
        {
                .er_name = "Admin Event Notification Queue Head Doorbell",
                .er_offset = ENAHW_REG_AENQ_HEAD_DB
        },
        {
                .er_name = "Admin Event Notification Queue Tail",
                .er_offset = ENAHW_REG_AENQ_TAIL
        },
        {
                .er_name = "Gap 0x48",
                .er_offset = ENAHW_REG_GAP_48
        },
        {
                .er_name = "Interrupt Mask (disable interrupts)",
                .er_offset = ENAHW_REG_INTERRUPT_MASK
        },
        {
                .er_name = "Gap 0x50",
                .er_offset = ENAHW_REG_GAP_50
        },
        {
                .er_name = "Device Control",
                .er_offset = ENAHW_REG_DEV_CTL
        },
        {
                .er_name = "Device Status",
                .er_offset = ENAHW_REG_DEV_STS
        },
        {
                .er_name = "MMIO Register Read",
                .er_offset = ENAHW_REG_MMIO_REG_READ
        },
        {
                .er_name = "MMIO Response Address Low",
                .er_offset = ENAHW_REG_MMIO_RESP_LO
        },
        {
                .er_name = "MMIO Response Address High",
                .er_offset = ENAHW_REG_MMIO_RESP_HI
        },
        {
                .er_name = "RSS Indirection Entry Update",
                .er_offset = ENAHW_REG_RSS_IND_ENTRY_UPDATE
        },
};

void
ena_update_regcache(ena_t *ena)
{
        for (uint_t i = 0; i < ENAHW_NUM_REGS; i++) {
                ena_reg_t *r = &ena->ena_reg[i];

                r->er_value = ena_hw_bar_read32(ena, r->er_offset);
        }
}

void
ena_init_regcache(ena_t *ena)
{
        bcopy(reg_cache_template, ena->ena_reg, sizeof (ena->ena_reg));
        ena_update_regcache(ena);
}
#endif /* DEBUG */