/* * 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 /* 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); }