1/* SPDX-License-Identifier: BSD-3-Clause */ 2/* Copyright (c) 2024, Intel Corporation 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, 9 * this list of conditions and the following disclaimer. 10 * 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * 3. Neither the name of the Intel Corporation nor the names of its 16 * contributors may be used to endorse or promote products derived from 17 * this software without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 23 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32/** 33 * @file ice_osdep.c 34 * @brief Functions used to implement OS compatibility layer 35 * 36 * Contains functions used by ice_osdep.h to implement the OS compatibility 37 * layer used by some of the hardware files. Specifically, it is for the bits 38 * of OS compatibility which don't make sense as macros or inline functions. 39 */ 40 41#include "ice_common.h" 42#include "ice_iflib.h" 43#include <machine/stdarg.h> 44#include <sys/time.h> 45 46/** 47 * @var M_ICE_OSDEP 48 * @brief OS compatibility layer allocation type 49 * 50 * malloc(9) allocation type used by the OS compatibility layer for 51 * distinguishing allocations by this layer from those of the rest of the 52 * driver. 53 */ 54MALLOC_DEFINE(M_ICE_OSDEP, "ice-osdep", "Intel(R) 100Gb Network Driver osdep allocations"); 55 56/** 57 * @var ice_lock_count 58 * @brief Global count of # of ice_lock mutexes initialized 59 * 60 * A global count of the total number of times that ice_init_lock has been 61 * called. This is used to generate unique lock names for each ice_lock, to 62 * aid in witness lock checking. 63 */ 64u16 ice_lock_count = 0; 65 66static void ice_dmamap_cb(void *arg, bus_dma_segment_t * segs, int __unused nseg, int error); 67 68/** 69 * ice_hw_to_dev - Given a hw private struct, find the associated device_t 70 * @hw: the hardware private structure 71 * 72 * Given a hw structure pointer, lookup the softc and extract the device 73 * pointer. Assumes that hw is embedded within the ice_softc, instead of being 74 * allocated separately, so that __containerof math will work. 75 * 76 * This can't be defined in ice_osdep.h as it depends on the complete 77 * definition of struct ice_softc. That can't be easily included in 78 * ice_osdep.h without creating circular header dependencies. 79 */ 80device_t 81ice_hw_to_dev(struct ice_hw *hw) { 82 struct ice_softc *sc = __containerof(hw, struct ice_softc, hw); 83 84 return sc->dev; 85} 86 87/** 88 * ice_debug - Log a debug message if the type is enabled 89 * @hw: device private hardware structure 90 * @mask: the debug message type 91 * @fmt: printf format specifier 92 * 93 * Check if hw->debug_mask has enabled the given message type. If so, log the 94 * message to the console using vprintf. Mimic the output of device_printf by 95 * using device_print_prettyname(). 96 */ 97void 98ice_debug(struct ice_hw *hw, uint64_t mask, char *fmt, ...) 99{ 100 device_t dev = ice_hw_to_dev(hw); 101 va_list args; 102 103 if (!(mask & hw->debug_mask)) 104 return; 105 106 device_print_prettyname(dev); 107 va_start(args, fmt); 108 vprintf(fmt, args); 109 va_end(args); 110} 111 112/** 113 * ice_debug_array - Format and print an array of values to the console 114 * @hw: private hardware structure 115 * @mask: the debug message type 116 * @rowsize: preferred number of rows to use 117 * @groupsize: preferred size in bytes to print each chunk 118 * @buf: the array buffer to print 119 * @len: size of the array buffer 120 * 121 * Format the given array as a series of uint8_t values with hexadecimal 122 * notation and log the contents to the console log. 123 * 124 * TODO: Currently only supports a group size of 1, due to the way hexdump is 125 * implemented. 126 */ 127void 128ice_debug_array(struct ice_hw *hw, uint64_t mask, uint32_t rowsize, 129 uint32_t __unused groupsize, uint8_t *buf, size_t len) 130{ 131 device_t dev = ice_hw_to_dev(hw); 132 char prettyname[20]; 133 134 if (!(mask & hw->debug_mask)) 135 return; 136 137 /* Format the device header to a string */ 138 snprintf(prettyname, sizeof(prettyname), "%s: ", device_get_nameunit(dev)); 139 140 /* Make sure the row-size isn't too large */ 141 if (rowsize > 0xFF) 142 rowsize = 0xFF; 143 144 hexdump(buf, len, prettyname, HD_OMIT_CHARS | rowsize); 145} 146 147/** 148 * ice_info_fwlog - Format and print an array of values to the console 149 * @hw: private hardware structure 150 * @rowsize: preferred number of rows to use 151 * @groupsize: preferred size in bytes to print each chunk 152 * @buf: the array buffer to print 153 * @len: size of the array buffer 154 * 155 * Format the given array as a series of uint8_t values with hexadecimal 156 * notation and log the contents to the console log. This variation is 157 * specific to firmware logging. 158 * 159 * TODO: Currently only supports a group size of 1, due to the way hexdump is 160 * implemented. 161 */ 162void 163ice_info_fwlog(struct ice_hw *hw, uint32_t rowsize, uint32_t __unused groupsize, 164 uint8_t *buf, size_t len) 165{ 166 device_t dev = ice_hw_to_dev(hw); 167 char prettyname[20]; 168 169 if (!ice_fwlog_supported(hw)) 170 return; 171 172 /* Format the device header to a string */ 173 snprintf(prettyname, sizeof(prettyname), "%s: FWLOG: ", 174 device_get_nameunit(dev)); 175 176 /* Make sure the row-size isn't too large */ 177 if (rowsize > 0xFF) 178 rowsize = 0xFF; 179 180 hexdump(buf, len, prettyname, HD_OMIT_CHARS | rowsize); 181} 182 183/** 184 * rd32 - Read a 32bit hardware register value 185 * @hw: the private hardware structure 186 * @reg: register address to read 187 * 188 * Read the specified 32bit register value from BAR0 and return its contents. 189 */ 190uint32_t 191rd32(struct ice_hw *hw, uint32_t reg) 192{ 193 struct ice_softc *sc = __containerof(hw, struct ice_softc, hw); 194 195 return bus_space_read_4(sc->bar0.tag, sc->bar0.handle, reg); 196} 197 198/** 199 * rd64 - Read a 64bit hardware register value 200 * @hw: the private hardware structure 201 * @reg: register address to read 202 * 203 * Read the specified 64bit register value from BAR0 and return its contents. 204 * 205 * @pre For 32-bit builds, assumes that the 64bit register read can be 206 * safely broken up into two 32-bit register reads. 207 */ 208uint64_t 209rd64(struct ice_hw *hw, uint32_t reg) 210{ 211 struct ice_softc *sc = __containerof(hw, struct ice_softc, hw); 212 uint64_t data; 213 214#ifdef __amd64__ 215 data = bus_space_read_8(sc->bar0.tag, sc->bar0.handle, reg); 216#else 217 /* 218 * bus_space_read_8 isn't supported on 32bit platforms, so we fall 219 * back to using two bus_space_read_4 calls. 220 */ 221 data = bus_space_read_4(sc->bar0.tag, sc->bar0.handle, reg); 222 data |= ((uint64_t)bus_space_read_4(sc->bar0.tag, sc->bar0.handle, reg + 4)) << 32; 223#endif 224 225 return data; 226} 227 228/** 229 * wr32 - Write a 32bit hardware register 230 * @hw: the private hardware structure 231 * @reg: the register address to write to 232 * @val: the 32bit value to write 233 * 234 * Write the specified 32bit value to a register address in BAR0. 235 */ 236void 237wr32(struct ice_hw *hw, uint32_t reg, uint32_t val) 238{ 239 struct ice_softc *sc = __containerof(hw, struct ice_softc, hw); 240 241 bus_space_write_4(sc->bar0.tag, sc->bar0.handle, reg, val); 242} 243 244/** 245 * wr64 - Write a 64bit hardware register 246 * @hw: the private hardware structure 247 * @reg: the register address to write to 248 * @val: the 64bit value to write 249 * 250 * Write the specified 64bit value to a register address in BAR0. 251 * 252 * @pre For 32-bit builds, assumes that the 64bit register write can be safely 253 * broken up into two 32-bit register writes. 254 */ 255void 256wr64(struct ice_hw *hw, uint32_t reg, uint64_t val) 257{ 258 struct ice_softc *sc = __containerof(hw, struct ice_softc, hw); 259 260#ifdef __amd64__ 261 bus_space_write_8(sc->bar0.tag, sc->bar0.handle, reg, val); 262#else 263 uint32_t lo_val, hi_val; 264 265 /* 266 * bus_space_write_8 isn't supported on 32bit platforms, so we fall 267 * back to using two bus_space_write_4 calls. 268 */ 269 lo_val = (uint32_t)val; 270 hi_val = (uint32_t)(val >> 32); 271 bus_space_write_4(sc->bar0.tag, sc->bar0.handle, reg, lo_val); 272 bus_space_write_4(sc->bar0.tag, sc->bar0.handle, reg + 4, hi_val); 273#endif 274} 275 276/** 277 * ice_usec_delay - Delay for the specified number of microseconds 278 * @time: microseconds to delay 279 * @sleep: if true, sleep where possible 280 * 281 * If sleep is true, and if the current thread is allowed to sleep, pause so 282 * that another thread can execute. Otherwise, use DELAY to spin the thread 283 * instead. 284 */ 285void 286ice_usec_delay(uint32_t time, bool sleep) 287{ 288 if (sleep && THREAD_CAN_SLEEP()) 289 pause("ice_usec_delay", USEC_2_TICKS(time)); 290 else 291 DELAY(time); 292} 293 294/** 295 * ice_msec_delay - Delay for the specified number of milliseconds 296 * @time: milliseconds to delay 297 * @sleep: if true, sleep where possible 298 * 299 * If sleep is true, and if the current thread is allowed to sleep, pause so 300 * that another thread can execute. Otherwise, use DELAY to spin the thread 301 * instead. 302 */ 303void 304ice_msec_delay(uint32_t time, bool sleep) 305{ 306 if (sleep && THREAD_CAN_SLEEP()) 307 pause("ice_msec_delay", MSEC_2_TICKS(time)); 308 else 309 DELAY(time * 1000); 310} 311 312/** 313 * ice_msec_pause - pause (sleep) the thread for a time in milliseconds 314 * @time: milliseconds to sleep 315 * 316 * Wrapper for ice_msec_delay with sleep set to true. 317 */ 318void 319ice_msec_pause(uint32_t time) 320{ 321 ice_msec_delay(time, true); 322} 323 324/** 325 * ice_msec_spin - Spin the thread for a time in milliseconds 326 * @time: milliseconds to delay 327 * 328 * Wrapper for ice_msec_delay with sleep sent to false. 329 */ 330void 331ice_msec_spin(uint32_t time) 332{ 333 ice_msec_delay(time, false); 334} 335 336/******************************************************************** 337 * Manage DMA'able memory. 338 *******************************************************************/ 339 340/** 341 * ice_dmamap_cb - Callback function DMA maps 342 * @arg: pointer to return the segment address 343 * @segs: the segments array 344 * @nseg: number of segments in the array 345 * @error: error code 346 * 347 * Callback used by the bus DMA code to obtain the segment address. 348 */ 349static void 350ice_dmamap_cb(void *arg, bus_dma_segment_t * segs, int __unused nseg, int error) 351{ 352 if (error) 353 return; 354 *(bus_addr_t *) arg = segs->ds_addr; 355 return; 356} 357 358/** 359 * ice_alloc_dma_mem - Request OS to allocate DMA memory 360 * @hw: private hardware structure 361 * @mem: structure defining the DMA memory request 362 * @size: the allocation size 363 * 364 * Allocates some memory for DMA use. Use the FreeBSD bus DMA interface to 365 * track this memory using a bus DMA tag and map. 366 * 367 * Returns a pointer to the DMA memory address. 368 */ 369void * 370ice_alloc_dma_mem(struct ice_hw *hw, struct ice_dma_mem *mem, u64 size) 371{ 372 device_t dev = ice_hw_to_dev(hw); 373 int err; 374 375 err = bus_dma_tag_create(bus_get_dma_tag(dev), /* parent */ 376 1, 0, /* alignment, boundary */ 377 BUS_SPACE_MAXADDR, /* lowaddr */ 378 BUS_SPACE_MAXADDR, /* highaddr */ 379 NULL, NULL, /* filtfunc, filtfuncarg */ 380 size, /* maxsize */ 381 1, /* nsegments */ 382 size, /* maxsegsz */ 383 BUS_DMA_ALLOCNOW, /* flags */ 384 NULL, /* lockfunc */ 385 NULL, /* lockfuncarg */ 386 &mem->tag); 387 if (err != 0) { 388 device_printf(dev, 389 "ice_alloc_dma: bus_dma_tag_create failed, " 390 "error %s\n", ice_err_str(err)); 391 goto fail_0; 392 } 393 err = bus_dmamem_alloc(mem->tag, (void **)&mem->va, 394 BUS_DMA_NOWAIT | BUS_DMA_ZERO, &mem->map); 395 if (err != 0) { 396 device_printf(dev, 397 "ice_alloc_dma: bus_dmamem_alloc failed, " 398 "error %s\n", ice_err_str(err)); 399 goto fail_1; 400 } 401 err = bus_dmamap_load(mem->tag, mem->map, mem->va, 402 size, 403 ice_dmamap_cb, 404 &mem->pa, 405 BUS_DMA_NOWAIT); 406 if (err != 0) { 407 device_printf(dev, 408 "ice_alloc_dma: bus_dmamap_load failed, " 409 "error %s\n", ice_err_str(err)); 410 goto fail_2; 411 } 412 mem->size = size; 413 bus_dmamap_sync(mem->tag, mem->map, 414 BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE); 415 return (mem->va); 416fail_2: 417 bus_dmamem_free(mem->tag, mem->va, mem->map); 418fail_1: 419 bus_dma_tag_destroy(mem->tag); 420fail_0: 421 mem->map = NULL; 422 mem->tag = NULL; 423 return (NULL); 424} 425 426/** 427 * ice_free_dma_mem - Free DMA memory allocated by ice_alloc_dma_mem 428 * @hw: the hardware private structure 429 * @mem: DMA memory to free 430 * 431 * Release the bus DMA tag and map, and free the DMA memory associated with 432 * it. 433 */ 434void 435ice_free_dma_mem(struct ice_hw __unused *hw, struct ice_dma_mem *mem) 436{ 437 bus_dmamap_sync(mem->tag, mem->map, 438 BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); 439 bus_dmamap_unload(mem->tag, mem->map); 440 bus_dmamem_free(mem->tag, mem->va, mem->map); 441 bus_dma_tag_destroy(mem->tag); 442 mem->map = NULL; 443 mem->tag = NULL; 444} 445