xref: /freebsd/sys/dev/ice/ice_osdep.c (revision b985c9ca)
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