root/usr/src/uts/common/io/i40e/i40e_osdep.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 2015 OmniTI Computer Consulting, Inc. All rights reserved.
 * Copyright 2016 Joyent, Inc.
 */

#include "i40e_sw.h"
#include "i40e_type.h"
#include "i40e_alloc.h"
#include "i40e_osdep.h"

#include <sys/dtrace.h>

/* ARGSUSED */
i40e_status
i40e_allocate_virt_mem(struct i40e_hw *hw, struct i40e_virt_mem *mem, u32 size)
{
        mem->va = kmem_zalloc(size, KM_SLEEP);
        mem->size = size;
        return (I40E_SUCCESS);
}

/* ARGSUSED */
i40e_status
i40e_free_virt_mem(struct i40e_hw *hw, struct i40e_virt_mem *mem)
{
        if (mem->va != NULL)
                kmem_free(mem->va, mem->size);
        return (I40E_SUCCESS);
}

/* ARGSUSED */
i40e_status
i40e_allocate_dma_mem(struct i40e_hw *hw, struct i40e_dma_mem *mem,
    enum i40e_memory_type type, u64 size, u32 alignment)
{
        int rc;
        i40e_t *i40e = OS_DEP(hw)->ios_i40e;
        dev_info_t *dip = i40e->i40e_dip;
        size_t len;
        ddi_dma_cookie_t cookie;
        uint_t cookie_num;
        ddi_dma_attr_t attr;

        /*
         * Because we need to honor the specified alignment, we need to
         * dynamically construct the attributes. We save the alignment for
         * debugging purposes.
         */
        bcopy(&i40e->i40e_static_dma_attr, &attr, sizeof (ddi_dma_attr_t));
        attr.dma_attr_align = alignment;
        mem->idm_alignment = alignment;
        rc = ddi_dma_alloc_handle(dip, &i40e->i40e_static_dma_attr,
            DDI_DMA_DONTWAIT, NULL, &mem->idm_dma_handle);
        if (rc != DDI_SUCCESS) {
                mem->idm_dma_handle = NULL;
                i40e_error(i40e, "failed to allocate DMA handle for common "
                    "code: %d", rc);

                /*
                 * Swallow unknown errors and treat them like we do
                 * DDI_DMA_NORESOURCES, in other words, a memory error.
                 */
                if (rc == DDI_DMA_BADATTR)
                        return (I40E_ERR_PARAM);
                return (I40E_ERR_NO_MEMORY);
        }

        rc = ddi_dma_mem_alloc(mem->idm_dma_handle, size,
            &i40e->i40e_buf_acc_attr, DDI_DMA_STREAMING, DDI_DMA_DONTWAIT,
            NULL, (caddr_t *)&mem->va, &len, &mem->idm_acc_handle);
        if (rc != DDI_SUCCESS) {
                mem->idm_acc_handle = NULL;
                mem->va = NULL;
                ASSERT(mem->idm_dma_handle != NULL);
                ddi_dma_free_handle(&mem->idm_dma_handle);
                mem->idm_dma_handle = NULL;

                i40e_error(i40e, "failed to allocate %" PRIu64 " bytes of DMA "
                    "memory for common code", size);
                return (I40E_ERR_NO_MEMORY);
        }

        bzero(mem->va, len);

        rc = ddi_dma_addr_bind_handle(mem->idm_dma_handle, NULL, mem->va, len,
            DDI_DMA_RDWR | DDI_DMA_STREAMING, DDI_DMA_DONTWAIT, NULL,
            &cookie, &cookie_num);
        if (rc != DDI_DMA_MAPPED) {
                mem->pa = 0;
                ASSERT(mem->idm_acc_handle != NULL);
                ddi_dma_mem_free(&mem->idm_acc_handle);
                mem->idm_acc_handle = NULL;
                mem->va = NULL;
                ASSERT(mem->idm_dma_handle != NULL);
                ddi_dma_free_handle(&mem->idm_dma_handle);
                mem->idm_dma_handle = NULL;

                i40e_error(i40e, "failed to bind %ld byte sized dma region: %d",
                    len, rc);
                switch (rc) {
                case DDI_DMA_INUSE:
                        return (I40E_ERR_NOT_READY);
                case DDI_DMA_TOOBIG:
                        return (I40E_ERR_INVALID_SIZE);
                case DDI_DMA_NOMAPPING:
                case DDI_DMA_NORESOURCES:
                default:
                        return (I40E_ERR_NO_MEMORY);
                }
        }

        ASSERT(cookie_num == 1);
        mem->pa = cookie.dmac_laddress;
        /*
         * Lint doesn't like this because the common code gives us a uint64_t as
         * input, but the common code then asks us to assign it to a size_t. So
         * lint's right, but in this case there isn't much we can do.
         */
        mem->size = (size_t)size;

        return (I40E_SUCCESS);
}

/* ARGSUSED */
i40e_status
i40e_free_dma_mem(struct i40e_hw *hw, struct i40e_dma_mem *mem)
{
        if (mem->pa != 0) {
                VERIFY(mem->idm_dma_handle != NULL);
                (void) ddi_dma_unbind_handle(mem->idm_dma_handle);
                mem->pa = 0;
                mem->size = 0;
        }

        if (mem->idm_acc_handle != NULL) {
                ddi_dma_mem_free(&mem->idm_acc_handle);
                mem->idm_acc_handle = NULL;
                mem->va = NULL;
        }

        if (mem->idm_dma_handle != NULL) {
                ddi_dma_free_handle(&mem->idm_dma_handle);
                mem->idm_dma_handle = NULL;
        }

        /*
         * Watch out for sloppiness.
         */
        ASSERT(mem->pa == 0);
        ASSERT(mem->va == NULL);
        ASSERT(mem->size == 0);
        mem->idm_alignment = UINT32_MAX;

        return (I40E_SUCCESS);
}

/*
 * The common code wants to initialize its 'spinlocks' here, aka adaptive
 * mutexes. At this time these are only used to maintain the adminq's data and
 * as such it will only be used outside of interrupt context and even then,
 * we're not going to actually end up ever doing anything above lock level and
 * up in doing stuff with high level interrupts.
 */
void
i40e_init_spinlock(struct i40e_spinlock *lock)
{
        mutex_init(&lock->ispl_mutex, NULL, MUTEX_DRIVER, NULL);
}

void
i40e_acquire_spinlock(struct i40e_spinlock *lock)
{
        mutex_enter(&lock->ispl_mutex);
}

void
i40e_release_spinlock(struct i40e_spinlock *lock)
{
        mutex_exit(&lock->ispl_mutex);
}

void
i40e_destroy_spinlock(struct i40e_spinlock *lock)
{
        mutex_destroy(&lock->ispl_mutex);
}

boolean_t
i40e_set_hw_bus_info(struct i40e_hw *hw)
{
        uint8_t pcie_id = PCI_CAP_ID_PCI_E;
        uint16_t pcie_cap, value;
        int status;

        /* locate the pci-e capability block */
        status = pci_lcap_locate((OS_DEP(hw))->ios_cfg_handle, pcie_id,
            &pcie_cap);
        if (status != DDI_SUCCESS) {
                i40e_error(OS_DEP(hw)->ios_i40e, "failed to locate PCIe "
                    "capability block: %d",
                    status);
                return (B_FALSE);
        }

        value = pci_config_get16(OS_DEP(hw)->ios_cfg_handle,
            pcie_cap + PCIE_LINKSTS);

        i40e_set_pci_config_data(hw, value);

        return (B_TRUE);
}

/* ARGSUSED */
void
i40e_debug(void *hw, u32 mask, char *fmt, ...)
{
        char buf[1024];
        va_list args;

        va_start(args, fmt);
        (void) vsnprintf(buf, sizeof (buf), fmt, args);
        va_end(args);

        DTRACE_PROBE2(i40e__debug, uint32_t, mask, char *, buf);
}