1 /******************************************************************************
2
3 Copyright (c) 2013-2014, Intel Corporation
4 All rights reserved.
5
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions are met:
8
9 1. Redistributions of source code must retain the above copyright notice,
10 this list of conditions and the following disclaimer.
11
12 2. Redistributions in binary form must reproduce the above copyright
13 notice, this list of conditions and the following disclaimer in the
14 documentation and/or other materials provided with the distribution.
15
16 3. Neither the name of the Intel Corporation nor the names of its
17 contributors may be used to endorse or promote products derived from
18 this software without specific prior written permission.
19
20 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
24 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 POSSIBILITY OF SUCH DAMAGE.
31
32 ******************************************************************************/
33 /*$FreeBSD: head/sys/dev/ixl/i40e_osdep.c 283119 2015-05-19 18:35:18Z jhb $*/
34
35 /********************************************************************
36 * Manage DMA'able memory.
37 *******************************************************************/
38
39 #include "i40e_sw.h"
40 #include "i40e_type.h"
41 #include "i40e_alloc.h"
42 #include "i40e_osdep.h"
43
44 /* NOTE: No sparc to test on, but I'm drawing inspiration from ixgbe here. */
45 #ifdef __sparc
46 #define I40E_DMA_ALIGNMENT 0x0000000000002000ull
47 #else
48 #define I40E_DMA_ALIGNMENT 0x0000000000001000ull
49 #endif
50
51 /*
52 * DMA attributes for rx buffers.
53 * Needs to be viewable for i40e_set_fma_flags().
54 */
55 ddi_dma_attr_t i40e_buf_dma_attr = {
56 DMA_ATTR_V0, /* version number */
57 0x0000000000000000ull, /* low address */
58 0xFFFFFFFFFFFFFFFFull, /* high address */
59 0x00000000FFFFFFFFull, /* dma counter max */
60 I40E_DMA_ALIGNMENT, /* alignment */
61 0x00000FFF, /* burst sizes */
62 0x00000001, /* minimum transfer size */
63 0x00000000FFFFFFFFull, /* maximum transfer size */
64 0xFFFFFFFFFFFFFFFFull, /* maximum segment size */
65 1, /* scatter/gather list length */
66 0x00000001, /* granularity */
67 DDI_DMA_FLAGERR /* DMA flags */
68 };
69
70 /*
71 * DMA access attributes for buffers.
72 */
73 static ddi_device_acc_attr_t i40e_buf_acc_attr = {
74 DDI_DEVICE_ATTR_V0,
75 DDI_NEVERSWAP_ACC,
76 DDI_STRICTORDER_ACC
77 };
78
79 i40e_status
80 i40e_allocate_virt_mem(struct i40e_hw *hw, struct i40e_virt_mem *mem, u32 size)
81 {
82 /* XXX KEBE SAYS -- should we do this? Or instead use a magazine? */
83 /*
84 * Also, what about prioritization? Use KM_SLEEP? Or instead use
85 * KM_NOSLEEP | KM_NORMALPRI ?
86 */
87 mem->va = kmem_alloc(size, KM_SLEEP);
88 mem->size = size;
89 return (I40E_SUCCESS);
90 }
91
92 i40e_status
93 i40e_free_virt_mem(struct i40e_hw *hw, struct i40e_virt_mem *mem)
94 {
95 if (mem->va != NULL)
96 kmem_free(mem->va, mem->size);
97 return (I40E_SUCCESS);
98 }
99
100 i40e_status
101 i40e_allocate_dma_mem(struct i40e_hw *hw, struct i40e_dma_mem *mem,
102 enum i40e_memory_type type, u64 size, u32 alignment)
103 {
104 int rc;
105 i40e_t *i40e = OS_DEP(hw)->i40e;
106 dev_info_t *dip = i40e->i40e_dip;
107 size_t len;
108 ddi_dma_cookie_t cookie;
109 uint_t cookie_num;
110
111 rc = ddi_dma_alloc_handle(dip, &i40e_buf_dma_attr, DDI_DMA_DONTWAIT,
112 NULL, &mem->dma_handle);
113 if (rc != DDI_SUCCESS) {
114 mem->dma_handle = NULL;
115 i40e_error(i40e, "ddi_dma_alloc_handle() failed: %d", rc);
116 /* Likely DDI_DMA_NORESOURCES, so return such... */
117 return (I40E_ERR_NO_MEMORY);
118 }
119
120 rc = ddi_dma_mem_alloc(mem->dma_handle, size, &i40e_buf_acc_attr,
121 DDI_DMA_STREAMING, DDI_DMA_DONTWAIT, NULL, (caddr_t *)&mem->va,
122 &len, &mem->acc_handle);
123 if (rc != DDI_SUCCESS) {
124 mem->acc_handle = NULL;
125 mem->va = NULL;
126 if (mem->dma_handle != NULL) {
127 ddi_dma_free_handle(&mem->dma_handle);
128 mem->dma_handle = NULL;
129 }
130 i40e_error(i40e, "ddi_dma_mem_alloc() failed: %d", rc);
131 /* rc is only DDI_FAILURE, so again, return NO_MEMORY. */
132 return (I40E_ERR_NO_MEMORY);
133 }
134
135 rc = ddi_dma_addr_bind_handle(mem->dma_handle, NULL, mem->va, len,
136 DDI_DMA_RDWR | DDI_DMA_STREAMING, DDI_DMA_DONTWAIT, NULL,
137 &cookie, &cookie_num);
138 if (rc != DDI_DMA_MAPPED) {
139 mem->pa = NULL;
140 if (mem->acc_handle == NULL) {
141 ddi_dma_mem_free(&mem->acc_handle);
142 mem->acc_handle = NULL;
143 mem->va = NULL;
144 }
145 if (mem->dma_handle != NULL) {
146 ddi_dma_free_handle(&mem->dma_handle);
147 mem->dma_handle = NULL;
148 }
149 i40e_error(i40e, "ddi_dma_addr_bind_handle() failed: %d", rc);
150 /* Multiple return code here demand multiple returns. */
151 switch (rc) {
152 case DDI_DMA_INUSE:
153 return (I40E_ERR_NOT_READY);
154 case DDI_DMA_TOOBIG:
155 return (I40E_ERR_INVALID_SIZE);
156 case DDI_DMA_NOMAPPING:
157 case DDI_DMA_NORESOURCES:
158 default:
159 return (I40E_ERR_NO_MEMORY);
160 }
161 /* NOTREACHED */
162 }
163
164 ASSERT(cookie_num == 1); /* From ixgbe.. why?!? */
165 mem->pa = cookie.dmac_laddress;
166 mem->size = len;
167 /* XXX KEBE SAYS ixgbe's object has a "len" -- why?!? */
168
169 return (I40E_SUCCESS);
170 }
171
172 i40e_status
173 i40e_free_dma_mem(struct i40e_hw *hw, struct i40e_dma_mem *mem)
174 {
175 if (mem->dma_handle != NULL) {
176 /* XXX KEBE ASKS What should I return if unbind fails? */
177 if (ddi_dma_unbind_handle(mem->dma_handle) != DDI_SUCCESS)
178 return (I40E_ERR_RESET_FAILED);
179 mem->pa = NULL;
180 } else {
181 return (I40E_SUCCESS);
182 }
183
184 if (mem->acc_handle != NULL) {
185 ddi_dma_mem_free(&mem->acc_handle);
186 mem->acc_handle = NULL;
187 mem->va = NULL;
188 }
189
190 if (mem->dma_handle != NULL) {
191 ddi_dma_free_handle(&mem->dma_handle);
192 mem->dma_handle = NULL;
193 }
194
195 mem->size = 0;
196 /* XXX KEBE SAYS see i40e_allocate_dma_mem() for question as to why? */
197 /* mem->len = 0; */
198
199 return (I40E_SUCCESS);
200 }
201
202 void
203 i40e_init_spinlock(struct i40e_spinlock *lock, struct i40e_hw *hw)
204 {
205 i40e_t *i40e = OS_DEP(hw)->i40e;
206 void *arg;
207
208 /*
209 * I MAY very well not have i40e_intr_pri *set* before this gets
210 * called. In that case, just set priority = 0, and assume that the
211 * caller (i40e setup code) will call functions that
212 * dispose-then-initialize spinlocks. We also assume that the caller
213 * is in attach context, and will HAVE INTERRUPTS ALLOCATED IN
214 * ATTACH CONTEXT when disposing-then-initializing spinlocks.
215 *
216 * Currently, the only caller of this is via i40e_common_code_init(),
217 * in particular the i40e_init_adminq() common-code-call.
218 */
219 if (i40e->i40e_intr_handles == NULL) {
220 /* Allocate priority 0 for now. */
221 arg = DDI_INTR_PRI(0);
222 } else {
223 /* Aha! We have enough to allocate proper MUTEX_DRIVER. */
224 arg = DDI_INTR_PRI(i40e->i40e_intr_pri);
225 }
226 mutex_init(&lock->mutex, NULL, MUTEX_DRIVER, arg);
227 }
228
229 void
230 i40e_acquire_spinlock(struct i40e_spinlock *lock)
231 {
232 mutex_enter(&lock->mutex);
233 }
234
235 void
236 i40e_release_spinlock(struct i40e_spinlock *lock)
237 {
238 mutex_exit(&lock->mutex);
239 }
240
241 void
242 i40e_destroy_spinlock(struct i40e_spinlock *lock)
243 {
244 mutex_destroy(&lock->mutex);
245 }
246
247 boolean_t
248 i40e_set_hw_bus_info(struct i40e_hw *hw)
249 {
250 uint8_t pcie_id = PCI_CAP_ID_PCI_E;
251 uint16_t pcie_cap, value;
252 int status;
253
254 /* locate the pci-e capability block */
255 status = pci_lcap_locate((OS_DEP(hw))->cfg_handle, pcie_id, &pcie_cap);
256 if (status != DDI_SUCCESS) {
257 i40e_error(OS_DEP(hw)->i40e, "pci_lcap_locate() failed %d",
258 status);
259 return (B_FALSE);
260 }
261
262 /*
263 * Get the low 16 bits. PCIe is little-endian so just do a get16()
264 * on things.
265 */
266 value = pci_config_get16(OS_DEP(hw)->cfg_handle,
267 (pcie_cap + PCIE_LINKSTS));
268
269 i40e_set_pci_config_data(hw, value);
270
271 return (B_TRUE);
272 }
273
274 /*
275 * RENAMED from i40e_debug_d because the SunPRO compilers don't support
276 * ##__VA_ARGS__ to solve the trailing comma problem.
277 */
278 void i40e_debug(void *hw, u32 mask, char *fmt, ...)
279 {
280 char buf[512];
281 va_list args;
282
283 if (!(mask & ((struct i40e_hw *)hw)->debug_mask))
284 return;
285
286 va_start(args, fmt);
287 vsnprintf(buf, sizeof(buf), fmt, args);
288 va_end(args);
289
290 /* the debug string is already formatted with a newline */
291 printf("%s", buf);
292 }