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