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