Changeset View
Changeset View
Standalone View
Standalone View
sys/dev/ice/ice_osdep.c
- This file was added.
/* SPDX-License-Identifier: BSD-3-Clause */ | |||||
/* Copyright (c) 2020, Intel Corporation | |||||
* All rights reserved. | |||||
* | |||||
* Redistribution and use in source and binary forms, with or without | |||||
* modification, are permitted provided that the following conditions are met: | |||||
* | |||||
* 1. Redistributions of source code must retain the above copyright notice, | |||||
* this list of conditions and the following disclaimer. | |||||
* | |||||
* 2. Redistributions in binary form must reproduce the above copyright | |||||
* notice, this list of conditions and the following disclaimer in the | |||||
* documentation and/or other materials provided with the distribution. | |||||
* | |||||
* 3. Neither the name of the Intel Corporation nor the names of its | |||||
* contributors may be used to endorse or promote products derived from | |||||
* this software without specific prior written permission. | |||||
* | |||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE | |||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | |||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | |||||
* POSSIBILITY OF SUCH DAMAGE. | |||||
*/ | |||||
/*$FreeBSD$*/ | |||||
/** | |||||
* @file ice_osdep.c | |||||
* @brief Functions used to implement OS compatibility layer | |||||
* | |||||
* Contains functions used by ice_osdep.h to implement the OS compatibility | |||||
* layer used by some of the hardware files. Specifically, it is for the bits | |||||
* of OS compatibility which don't make sense as macros or inline functions. | |||||
*/ | |||||
#include "ice_common.h" | |||||
#include "ice_iflib.h" | |||||
#include <machine/stdarg.h> | |||||
#include <sys/time.h> | |||||
/** | |||||
* @var M_ICE_OSDEP | |||||
* @brief OS compatibility layer allocation type | |||||
* | |||||
* malloc(9) allocation type used by the OS compatibility layer for | |||||
* distinguishing allocations by this layer from those of the rest of the | |||||
* driver. | |||||
*/ | |||||
MALLOC_DEFINE(M_ICE_OSDEP, "ice-osdep", "Intel(R) 100Gb Network Driver osdep allocations"); | |||||
/** | |||||
* @var ice_lock_count | |||||
* @brief Global count of # of ice_lock mutexes initialized | |||||
* | |||||
* A global count of the total number of times that ice_init_lock has been | |||||
* called. This is used to generate unique lock names for each ice_lock, to | |||||
* aid in witness lock checking. | |||||
*/ | |||||
u16 ice_lock_count = 0; | |||||
static void ice_dmamap_cb(void *arg, bus_dma_segment_t * segs, int __unused nseg, int error); | |||||
/** | |||||
* ice_hw_to_dev - Given a hw private struct, find the associated device_t | |||||
* @hw: the hardware private structure | |||||
* | |||||
* Given a hw structure pointer, lookup the softc and extract the device | |||||
* pointer. Assumes that hw is embedded within the ice_softc, instead of being | |||||
* allocated separately, so that __containerof math will work. | |||||
* | |||||
* This can't be defined in ice_osdep.h as it depends on the complete | |||||
* definition of struct ice_softc. That can't be easily included in | |||||
* ice_osdep.h without creating circular header dependencies. | |||||
*/ | |||||
device_t | |||||
ice_hw_to_dev(struct ice_hw *hw) { | |||||
struct ice_softc *sc = __containerof(hw, struct ice_softc, hw); | |||||
return sc->dev; | |||||
} | |||||
/** | |||||
* ice_debug - Log a debug message if the type is enabled | |||||
* @hw: device private hardware structure | |||||
* @mask: the debug message type | |||||
* @fmt: printf format specifier | |||||
* | |||||
* Check if hw->debug_mask has enabled the given message type. If so, log the | |||||
* message to the console using vprintf. Mimic the output of device_printf by | |||||
* using device_print_prettyname(). | |||||
*/ | |||||
void | |||||
ice_debug(struct ice_hw *hw, uint64_t mask, char *fmt, ...) | |||||
{ | |||||
device_t dev = ice_hw_to_dev(hw); | |||||
va_list args; | |||||
if (!(mask & hw->debug_mask)) | |||||
return; | |||||
device_print_prettyname(dev); | |||||
va_start(args, fmt); | |||||
vprintf(fmt, args); | |||||
va_end(args); | |||||
} | |||||
/** | |||||
* ice_debug_array - Format and print an array of values to the console | |||||
* @hw: private hardware structure | |||||
* @mask: the debug message type | |||||
* @rowsize: preferred number of rows to use | |||||
* @groupsize: preferred size in bytes to print each chunk | |||||
* @buf: the array buffer to print | |||||
* @len: size of the array buffer | |||||
* | |||||
* Format the given array as a series of uint8_t values with hexadecimal | |||||
* notation and log the contents to the console log. | |||||
* | |||||
* TODO: Currently only supports a group size of 1, due to the way hexdump is | |||||
* implemented. | |||||
*/ | |||||
void | |||||
ice_debug_array(struct ice_hw *hw, uint64_t mask, uint32_t rowsize, | |||||
uint32_t __unused groupsize, uint8_t *buf, size_t len) | |||||
{ | |||||
device_t dev = ice_hw_to_dev(hw); | |||||
char prettyname[20]; | |||||
if (!(mask & hw->debug_mask)) | |||||
return; | |||||
/* Format the device header to a string */ | |||||
snprintf(prettyname, sizeof(prettyname), "%s: ", device_get_nameunit(dev)); | |||||
/* Make sure the row-size isn't too large */ | |||||
if (rowsize > 0xFF) | |||||
rowsize = 0xFF; | |||||
hexdump(buf, len, prettyname, HD_OMIT_CHARS | rowsize); | |||||
} | |||||
/** | |||||
* rd32 - Read a 32bit hardware register value | |||||
* @hw: the private hardware structure | |||||
* @reg: register address to read | |||||
* | |||||
* Read the specified 32bit register value from BAR0 and return its contents. | |||||
*/ | |||||
uint32_t | |||||
rd32(struct ice_hw *hw, uint32_t reg) | |||||
{ | |||||
struct ice_softc *sc = __containerof(hw, struct ice_softc, hw); | |||||
return bus_space_read_4(sc->bar0.tag, sc->bar0.handle, reg); | |||||
} | |||||
/** | |||||
* rd64 - Read a 64bit hardware register value | |||||
* @hw: the private hardware structure | |||||
* @reg: register address to read | |||||
* | |||||
* Read the specified 64bit register value from BAR0 and return its contents. | |||||
* | |||||
* @pre For 32-bit builds, assumes that the 64bit register read can be | |||||
* safely broken up into two 32-bit register reads. | |||||
*/ | |||||
uint64_t | |||||
rd64(struct ice_hw *hw, uint32_t reg) | |||||
{ | |||||
struct ice_softc *sc = __containerof(hw, struct ice_softc, hw); | |||||
uint64_t data; | |||||
#ifdef __amd64__ | |||||
data = bus_space_read_8(sc->bar0.tag, sc->bar0.handle, reg); | |||||
#else | |||||
/* | |||||
* bus_space_read_8 isn't supported on 32bit platforms, so we fall | |||||
* back to using two bus_space_read_4 calls. | |||||
*/ | |||||
data = bus_space_read_4(sc->bar0.tag, sc->bar0.handle, reg); | |||||
data |= ((uint64_t)bus_space_read_4(sc->bar0.tag, sc->bar0.handle, reg + 4)) << 32; | |||||
#endif | |||||
return data; | |||||
} | |||||
/** | |||||
* wr32 - Write a 32bit hardware register | |||||
* @hw: the private hardware structure | |||||
* @reg: the register address to write to | |||||
* @val: the 32bit value to write | |||||
* | |||||
* Write the specified 32bit value to a register address in BAR0. | |||||
*/ | |||||
void | |||||
wr32(struct ice_hw *hw, uint32_t reg, uint32_t val) | |||||
{ | |||||
struct ice_softc *sc = __containerof(hw, struct ice_softc, hw); | |||||
bus_space_write_4(sc->bar0.tag, sc->bar0.handle, reg, val); | |||||
} | |||||
/** | |||||
* wr64 - Write a 64bit hardware register | |||||
* @hw: the private hardware structure | |||||
* @reg: the register address to write to | |||||
* @val: the 64bit value to write | |||||
* | |||||
* Write the specified 64bit value to a register address in BAR0. | |||||
* | |||||
* @pre For 32-bit builds, assumes that the 64bit register write can be safely | |||||
* broken up into two 32-bit register writes. | |||||
*/ | |||||
void | |||||
wr64(struct ice_hw *hw, uint32_t reg, uint64_t val) | |||||
{ | |||||
struct ice_softc *sc = __containerof(hw, struct ice_softc, hw); | |||||
#ifdef __amd64__ | |||||
bus_space_write_8(sc->bar0.tag, sc->bar0.handle, reg, val); | |||||
#else | |||||
uint32_t lo_val, hi_val; | |||||
/* | |||||
* bus_space_write_8 isn't supported on 32bit platforms, so we fall | |||||
* back to using two bus_space_write_4 calls. | |||||
*/ | |||||
lo_val = (uint32_t)val; | |||||
hi_val = (uint32_t)(val >> 32); | |||||
bus_space_write_4(sc->bar0.tag, sc->bar0.handle, reg, lo_val); | |||||
bus_space_write_4(sc->bar0.tag, sc->bar0.handle, reg + 4, hi_val); | |||||
#endif | |||||
} | |||||
/** | |||||
* ice_usec_delay - Delay for the specified number of microseconds | |||||
* @time: microseconds to delay | |||||
* @sleep: if true, sleep where possible | |||||
* | |||||
* If sleep is true, and if the current thread is allowed to sleep, pause so | |||||
* that another thread can execute. Otherwise, use DELAY to spin the thread | |||||
* instead. | |||||
*/ | |||||
void | |||||
ice_usec_delay(uint32_t time, bool sleep) | |||||
{ | |||||
if (sleep && THREAD_CAN_SLEEP()) | |||||
pause("ice_usec_delay", USEC_2_TICKS(time)); | |||||
else | |||||
DELAY(time); | |||||
} | |||||
/** | |||||
* ice_msec_delay - Delay for the specified number of milliseconds | |||||
* @time: milliseconds to delay | |||||
* @sleep: if true, sleep where possible | |||||
* | |||||
* If sleep is true, and if the current thread is allowed to sleep, pause so | |||||
* that another thread can execute. Otherwise, use DELAY to spin the thread | |||||
* instead. | |||||
*/ | |||||
void | |||||
ice_msec_delay(uint32_t time, bool sleep) | |||||
{ | |||||
if (sleep && THREAD_CAN_SLEEP()) | |||||
pause("ice_msec_delay", MSEC_2_TICKS(time)); | |||||
else | |||||
DELAY(time * 1000); | |||||
} | |||||
/** | |||||
* ice_msec_pause - pause (sleep) the thread for a time in milliseconds | |||||
* @time: milliseconds to sleep | |||||
* | |||||
* Wrapper for ice_msec_delay with sleep set to true. | |||||
*/ | |||||
void | |||||
ice_msec_pause(uint32_t time) | |||||
{ | |||||
ice_msec_delay(time, true); | |||||
} | |||||
/** | |||||
* ice_msec_spin - Spin the thread for a time in milliseconds | |||||
* @time: milliseconds to delay | |||||
* | |||||
* Wrapper for ice_msec_delay with sleep sent to false. | |||||
*/ | |||||
void | |||||
ice_msec_spin(uint32_t time) | |||||
{ | |||||
ice_msec_delay(time, false); | |||||
} | |||||
/******************************************************************** | |||||
* Manage DMA'able memory. | |||||
*******************************************************************/ | |||||
/** | |||||
* ice_dmamap_cb - Callback function DMA maps | |||||
* @arg: pointer to return the segment address | |||||
* @segs: the segments array | |||||
* @nseg: number of segments in the array | |||||
* @error: error code | |||||
* | |||||
* Callback used by the bus DMA code to obtain the segment address. | |||||
*/ | |||||
static void | |||||
ice_dmamap_cb(void *arg, bus_dma_segment_t * segs, int __unused nseg, int error) | |||||
{ | |||||
if (error) | |||||
return; | |||||
*(bus_addr_t *) arg = segs->ds_addr; | |||||
return; | |||||
} | |||||
/** | |||||
* ice_alloc_dma_mem - Request OS to allocate DMA memory | |||||
* @hw: private hardware structure | |||||
* @mem: structure defining the DMA memory request | |||||
* @size: the allocation size | |||||
* | |||||
* Allocates some memory for DMA use. Use the FreeBSD bus DMA interface to | |||||
* track this memory using a bus DMA tag and map. | |||||
* | |||||
* Returns a pointer to the DMA memory address. | |||||
*/ | |||||
void * | |||||
ice_alloc_dma_mem(struct ice_hw *hw, struct ice_dma_mem *mem, u64 size) | |||||
{ | |||||
device_t dev = ice_hw_to_dev(hw); | |||||
int err; | |||||
err = bus_dma_tag_create(bus_get_dma_tag(dev), /* parent */ | |||||
1, 0, /* alignment, boundary */ | |||||
BUS_SPACE_MAXADDR, /* lowaddr */ | |||||
BUS_SPACE_MAXADDR, /* highaddr */ | |||||
NULL, NULL, /* filtfunc, filtfuncarg */ | |||||
size, /* maxsize */ | |||||
1, /* nsegments */ | |||||
size, /* maxsegsz */ | |||||
BUS_DMA_ALLOCNOW, /* flags */ | |||||
NULL, /* lockfunc */ | |||||
NULL, /* lockfuncarg */ | |||||
&mem->tag); | |||||
if (err != 0) { | |||||
device_printf(dev, | |||||
"ice_alloc_dma: bus_dma_tag_create failed, " | |||||
"error %s\n", ice_err_str(err)); | |||||
goto fail_0; | |||||
} | |||||
err = bus_dmamem_alloc(mem->tag, (void **)&mem->va, | |||||
BUS_DMA_NOWAIT | BUS_DMA_ZERO, &mem->map); | |||||
if (err != 0) { | |||||
device_printf(dev, | |||||
"ice_alloc_dma: bus_dmamem_alloc failed, " | |||||
"error %s\n", ice_err_str(err)); | |||||
goto fail_1; | |||||
} | |||||
err = bus_dmamap_load(mem->tag, mem->map, mem->va, | |||||
size, | |||||
ice_dmamap_cb, | |||||
&mem->pa, | |||||
BUS_DMA_NOWAIT); | |||||
if (err != 0) { | |||||
device_printf(dev, | |||||
"ice_alloc_dma: bus_dmamap_load failed, " | |||||
"error %s\n", ice_err_str(err)); | |||||
goto fail_2; | |||||
} | |||||
mem->size = size; | |||||
bus_dmamap_sync(mem->tag, mem->map, | |||||
BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE); | |||||
return (mem->va); | |||||
fail_2: | |||||
bus_dmamem_free(mem->tag, mem->va, mem->map); | |||||
fail_1: | |||||
bus_dma_tag_destroy(mem->tag); | |||||
fail_0: | |||||
mem->map = NULL; | |||||
mem->tag = NULL; | |||||
return (NULL); | |||||
} | |||||
/** | |||||
* ice_free_dma_mem - Free DMA memory allocated by ice_alloc_dma_mem | |||||
* @hw: the hardware private structure | |||||
* @mem: DMA memory to free | |||||
* | |||||
* Release the bus DMA tag and map, and free the DMA memory associated with | |||||
* it. | |||||
*/ | |||||
void | |||||
ice_free_dma_mem(struct ice_hw __unused *hw, struct ice_dma_mem *mem) | |||||
{ | |||||
bus_dmamap_sync(mem->tag, mem->map, | |||||
BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); | |||||
bus_dmamap_unload(mem->tag, mem->map); | |||||
bus_dmamem_free(mem->tag, mem->va, mem->map); | |||||
bus_dma_tag_destroy(mem->tag); | |||||
mem->map = NULL; | |||||
mem->tag = NULL; | |||||
} |