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 */ 54 MALLOC_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 */ 64 u16 ice_lock_count = 0; 65 66 static 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 */ 80 device_t 81 ice_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 */ 97 void 98 ice_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 */ 127 void 128 ice_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 */ 162 void 163 ice_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 */ 190 uint32_t 191 rd32(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 */ 208 uint64_t 209 rd64(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 */ 236 void 237 wr32(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 */ 255 void 256 wr64(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 */ 285 void 286 ice_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 */ 303 void 304 ice_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 */ 318 void 319 ice_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 */ 330 void 331 ice_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 */ 349 static void 350 ice_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 */ 369 void * 370 ice_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); 416 fail_2: 417 bus_dmamem_free(mem->tag, mem->va, mem->map); 418 fail_1: 419 bus_dma_tag_destroy(mem->tag); 420 fail_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 */ 434 void 435 ice_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