1 /******************************************************************************
   2 
   3   Copyright (c) 2013-2014, Intel Corporation 
   4   All rights reserved.
   5   
   6   Redistribution and use in source and binary forms, with or without 
   7   modification, are permitted provided that the following conditions are met:
   8   
   9    1. Redistributions of source code must retain the above copyright notice, 
  10       this list of conditions and the following disclaimer.
  11   
  12    2. Redistributions in binary form must reproduce the above copyright 
  13       notice, this list of conditions and the following disclaimer in the 
  14       documentation and/or other materials provided with the distribution.
  15   
  16    3. Neither the name of the Intel Corporation nor the names of its 
  17       contributors may be used to endorse or promote products derived from 
  18       this software without specific prior written permission.
  19   
  20   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  21   AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
  22   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
  23   ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
  24   LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
  25   CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
  26   SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
  27   INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
  28   CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
  29   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  30   POSSIBILITY OF SUCH DAMAGE.
  31 
  32 ******************************************************************************/
  33 /*$FreeBSD: head/sys/dev/ixl/i40e_osdep.c 283119 2015-05-19 18:35:18Z jhb $*/
  34 
  35 /********************************************************************
  36  * Manage DMA'able memory.
  37  *******************************************************************/
  38 
  39 #include "i40e_sw.h"
  40 #include "i40e_type.h"
  41 #include "i40e_alloc.h"
  42 #include "i40e_osdep.h"
  43 
  44 /* NOTE:  No sparc to test on, but I'm drawing inspiration from ixgbe here. */
  45 #ifdef __sparc
  46 #define I40E_DMA_ALIGNMENT      0x0000000000002000ull
  47 #else
  48 #define I40E_DMA_ALIGNMENT      0x0000000000001000ull
  49 #endif
  50 
  51 /*
  52  * DMA attributes for rx buffers.
  53  * Needs to be viewable for i40e_set_fma_flags().
  54  */
  55 ddi_dma_attr_t i40e_buf_dma_attr = {
  56         DMA_ATTR_V0,                    /* version number */
  57         0x0000000000000000ull,          /* low address */
  58         0xFFFFFFFFFFFFFFFFull,          /* high address */
  59         0x00000000FFFFFFFFull,          /* dma counter max */
  60         I40E_DMA_ALIGNMENT,             /* alignment */
  61         0x00000FFF,                     /* burst sizes */
  62         0x00000001,                     /* minimum transfer size */
  63         0x00000000FFFFFFFFull,          /* maximum transfer size */
  64         0xFFFFFFFFFFFFFFFFull,          /* maximum segment size  */
  65         1,                              /* scatter/gather list length */
  66         0x00000001,                     /* granularity */
  67         DDI_DMA_FLAGERR                 /* DMA flags */
  68 };
  69 
  70 /*
  71  * DMA access attributes for buffers.
  72  */
  73 static ddi_device_acc_attr_t i40e_buf_acc_attr = {
  74         DDI_DEVICE_ATTR_V0,
  75         DDI_NEVERSWAP_ACC,
  76         DDI_STRICTORDER_ACC
  77 };
  78 
  79 i40e_status
  80 i40e_allocate_virt_mem(struct i40e_hw *hw, struct i40e_virt_mem *mem, u32 size)
  81 {
  82         /* XXX KEBE SAYS -- should we do this? Or instead use a magazine? */
  83         /*
  84          * Also, what about prioritization? Use KM_SLEEP? Or instead use
  85          * KM_NOSLEEP | KM_NORMALPRI ?
  86          */
  87         mem->va = kmem_alloc(size, KM_SLEEP);
  88         mem->size = size;
  89         return (I40E_SUCCESS);
  90 }
  91 
  92 i40e_status
  93 i40e_free_virt_mem(struct i40e_hw *hw, struct i40e_virt_mem *mem)
  94 {
  95         if (mem->va != NULL)
  96                 kmem_free(mem->va, mem->size);
  97         return (I40E_SUCCESS);
  98 }
  99 
 100 i40e_status
 101 i40e_allocate_dma_mem(struct i40e_hw *hw, struct i40e_dma_mem *mem,
 102     enum i40e_memory_type type, u64 size, u32 alignment)
 103 {
 104         int rc;
 105         i40e_t *i40e = OS_DEP(hw)->i40e;
 106         dev_info_t *dip = i40e->i40e_dip;
 107         size_t len;
 108         ddi_dma_cookie_t cookie;
 109         uint_t cookie_num;
 110 
 111         rc = ddi_dma_alloc_handle(dip, &i40e_buf_dma_attr, DDI_DMA_DONTWAIT,
 112             NULL, &mem->dma_handle);
 113         if (rc != DDI_SUCCESS) {
 114                 mem->dma_handle = NULL;
 115                 i40e_error(i40e, "ddi_dma_alloc_handle() failed: %d", rc);
 116                 /* Likely DDI_DMA_NORESOURCES, so return such... */
 117                 return (I40E_ERR_NO_MEMORY);
 118         }
 119 
 120         rc = ddi_dma_mem_alloc(mem->dma_handle, size, &i40e_buf_acc_attr,
 121             DDI_DMA_STREAMING, DDI_DMA_DONTWAIT, NULL, (caddr_t *)&mem->va,
 122             &len, &mem->acc_handle);
 123         if (rc != DDI_SUCCESS) {
 124                 mem->acc_handle = NULL;
 125                 mem->va = NULL;
 126                 if (mem->dma_handle != NULL) {
 127                         ddi_dma_free_handle(&mem->dma_handle);
 128                         mem->dma_handle = NULL;
 129                 }
 130                 i40e_error(i40e, "ddi_dma_mem_alloc() failed: %d", rc);
 131                 /* rc is only DDI_FAILURE, so again, return NO_MEMORY. */
 132                 return (I40E_ERR_NO_MEMORY);
 133         }
 134 
 135         rc = ddi_dma_addr_bind_handle(mem->dma_handle, NULL, mem->va, len,
 136             DDI_DMA_RDWR | DDI_DMA_STREAMING, DDI_DMA_DONTWAIT, NULL,
 137             &cookie, &cookie_num);
 138         if (rc != DDI_DMA_MAPPED) {
 139                 mem->pa = NULL;
 140                 if (mem->acc_handle == NULL) {
 141                         ddi_dma_mem_free(&mem->acc_handle);
 142                         mem->acc_handle = NULL;
 143                         mem->va = NULL;
 144                 }
 145                 if (mem->dma_handle != NULL) {
 146                         ddi_dma_free_handle(&mem->dma_handle);
 147                         mem->dma_handle = NULL;
 148                 }
 149                 i40e_error(i40e, "ddi_dma_addr_bind_handle() failed: %d", rc);
 150                 /* Multiple return code here demand multiple returns. */
 151                 switch (rc) {
 152                 case DDI_DMA_INUSE:
 153                         return (I40E_ERR_NOT_READY);
 154                 case DDI_DMA_TOOBIG:
 155                         return (I40E_ERR_INVALID_SIZE);
 156                 case DDI_DMA_NOMAPPING:
 157                 case DDI_DMA_NORESOURCES:
 158                 default:
 159                         return (I40E_ERR_NO_MEMORY);
 160                 }
 161                 /* NOTREACHED */
 162         }
 163 
 164         ASSERT(cookie_num == 1);        /* From ixgbe.. why?!? */
 165         mem->pa = cookie.dmac_laddress;
 166         mem->size = len;
 167         /* XXX KEBE SAYS ixgbe's object has a "len" -- why?!? */
 168 
 169         return (I40E_SUCCESS);
 170 }
 171 
 172 i40e_status
 173 i40e_free_dma_mem(struct i40e_hw *hw, struct i40e_dma_mem *mem)
 174 {
 175         if (mem->dma_handle != NULL) {
 176                 /* XXX KEBE ASKS What should I return if unbind fails? */
 177                 if (ddi_dma_unbind_handle(mem->dma_handle) != DDI_SUCCESS)
 178                         return (I40E_ERR_RESET_FAILED);
 179                 mem->pa = NULL;
 180         } else {
 181                 return (I40E_SUCCESS);
 182         }
 183 
 184         if (mem->acc_handle != NULL) {
 185                 ddi_dma_mem_free(&mem->acc_handle);
 186                 mem->acc_handle = NULL;
 187                 mem->va = NULL;
 188         }
 189 
 190         if (mem->dma_handle != NULL) {
 191                 ddi_dma_free_handle(&mem->dma_handle);
 192                 mem->dma_handle = NULL;
 193         }
 194 
 195         mem->size = 0;
 196         /* XXX KEBE SAYS see i40e_allocate_dma_mem() for question as to why? */
 197         /* mem->len = 0; */
 198 
 199         return (I40E_SUCCESS);
 200 }
 201 
 202 void
 203 i40e_init_spinlock(struct i40e_spinlock *lock, struct i40e_hw *hw)
 204 {
 205         i40e_t *i40e = OS_DEP(hw)->i40e;
 206         void *arg;
 207 
 208         /*
 209          * I MAY very well not have i40e_intr_pri *set* before this gets
 210          * called.  In that case, just set priority = 0, and assume that the
 211          * caller (i40e setup code) will call functions that
 212          * dispose-then-initialize spinlocks.  We also assume that the caller
 213          * is in attach context, and will HAVE INTERRUPTS ALLOCATED IN
 214          * ATTACH CONTEXT when disposing-then-initializing spinlocks.
 215          *
 216          * Currently, the only caller of this is via i40e_common_code_init(),
 217          * in particular the i40e_init_adminq() common-code-call.
 218          */
 219         if (i40e->i40e_intr_handles == NULL) {
 220                 /* Allocate priority 0 for now. */
 221                 arg = DDI_INTR_PRI(0);
 222         } else {
 223                 /* Aha!  We have enough to allocate proper MUTEX_DRIVER. */
 224                 arg = DDI_INTR_PRI(i40e->i40e_intr_pri);
 225         }
 226         mutex_init(&lock->mutex, NULL, MUTEX_DRIVER, arg);
 227 }
 228 
 229 void
 230 i40e_acquire_spinlock(struct i40e_spinlock *lock)
 231 {
 232         mutex_enter(&lock->mutex);
 233 }
 234 
 235 void
 236 i40e_release_spinlock(struct i40e_spinlock *lock)
 237 {
 238         mutex_exit(&lock->mutex);
 239 }
 240 
 241 void
 242 i40e_destroy_spinlock(struct i40e_spinlock *lock)
 243 {
 244         mutex_destroy(&lock->mutex);
 245 }
 246 
 247 boolean_t
 248 i40e_set_hw_bus_info(struct i40e_hw *hw)
 249 {
 250         uint8_t pcie_id = PCI_CAP_ID_PCI_E;
 251         uint16_t pcie_cap, value;
 252         int status;
 253 
 254         /* locate the pci-e capability block */
 255         status = pci_lcap_locate((OS_DEP(hw))->cfg_handle, pcie_id, &pcie_cap);
 256         if (status != DDI_SUCCESS) {
 257                 i40e_error(OS_DEP(hw)->i40e, "pci_lcap_locate() failed %d",
 258                     status);
 259                 return (B_FALSE);
 260         }
 261 
 262         /*
 263          * Get the low 16 bits.  PCIe is little-endian so just do a get16()
 264          * on things.
 265          */
 266         value = pci_config_get16(OS_DEP(hw)->cfg_handle,
 267             (pcie_cap + PCIE_LINKSTS));
 268 
 269         i40e_set_pci_config_data(hw, value);
 270 
 271         return (B_TRUE);
 272 }
 273 
 274 /*
 275  * RENAMED from i40e_debug_d because the SunPRO compilers don't support
 276  * ##__VA_ARGS__ to solve the trailing comma problem.
 277 */
 278 void i40e_debug(void *hw, u32 mask, char *fmt, ...)
 279 {
 280         char buf[512];
 281         va_list args;
 282 
 283         if (!(mask & ((struct i40e_hw *)hw)->debug_mask))
 284                 return;
 285 
 286         va_start(args, fmt);
 287         vsnprintf(buf, sizeof(buf), fmt, args);
 288         va_end(args);
 289 
 290         /* the debug string is already formatted with a newline */
 291         printf("%s", buf);
 292 }