1 /*
2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
5 * 1.0 of the CDDL.
6 *
7 * A full copy of the text of the CDDL should have accompanied this
8 * source. A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
10 */
11
12 /*
13 * Copyright 2015 OmniTI Computer Consulting, Inc. All rights reserved.
14 * Copyright 2016 Joyent, Inc.
15 */
16
17 #include "i40e_sw.h"
18 #include "i40e_type.h"
19 #include "i40e_alloc.h"
20 #include "i40e_osdep.h"
21
22 #include <sys/dtrace.h>
23
24 /* ARGSUSED */
25 i40e_status
i40e_allocate_virt_mem(struct i40e_hw * hw,struct i40e_virt_mem * mem,u32 size)26 i40e_allocate_virt_mem(struct i40e_hw *hw, struct i40e_virt_mem *mem, u32 size)
27 {
28 mem->va = kmem_zalloc(size, KM_SLEEP);
29 mem->size = size;
30 return (I40E_SUCCESS);
31 }
32
33 /* ARGSUSED */
34 i40e_status
i40e_free_virt_mem(struct i40e_hw * hw,struct i40e_virt_mem * mem)35 i40e_free_virt_mem(struct i40e_hw *hw, struct i40e_virt_mem *mem)
36 {
37 if (mem->va != NULL)
38 kmem_free(mem->va, mem->size);
39 return (I40E_SUCCESS);
40 }
41
42 /* ARGSUSED */
43 i40e_status
i40e_allocate_dma_mem(struct i40e_hw * hw,struct i40e_dma_mem * mem,enum i40e_memory_type type,u64 size,u32 alignment)44 i40e_allocate_dma_mem(struct i40e_hw *hw, struct i40e_dma_mem *mem,
45 enum i40e_memory_type type, u64 size, u32 alignment)
46 {
47 int rc;
48 i40e_t *i40e = OS_DEP(hw)->ios_i40e;
49 dev_info_t *dip = i40e->i40e_dip;
50 size_t len;
51 ddi_dma_cookie_t cookie;
52 uint_t cookie_num;
53 ddi_dma_attr_t attr;
54
55 /*
56 * Because we need to honor the specified alignment, we need to
57 * dynamically construct the attributes. We save the alignment for
58 * debugging purposes.
59 */
60 bcopy(&i40e->i40e_static_dma_attr, &attr, sizeof (ddi_dma_attr_t));
61 attr.dma_attr_align = alignment;
62 mem->idm_alignment = alignment;
63 rc = ddi_dma_alloc_handle(dip, &i40e->i40e_static_dma_attr,
64 DDI_DMA_DONTWAIT, NULL, &mem->idm_dma_handle);
65 if (rc != DDI_SUCCESS) {
66 mem->idm_dma_handle = NULL;
67 i40e_error(i40e, "failed to allocate DMA handle for common "
68 "code: %d", rc);
69
70 /*
71 * Swallow unknown errors and treat them like we do
72 * DDI_DMA_NORESOURCES, in other words, a memory error.
73 */
74 if (rc == DDI_DMA_BADATTR)
75 return (I40E_ERR_PARAM);
76 return (I40E_ERR_NO_MEMORY);
77 }
78
79 rc = ddi_dma_mem_alloc(mem->idm_dma_handle, size,
80 &i40e->i40e_buf_acc_attr, DDI_DMA_STREAMING, DDI_DMA_DONTWAIT,
81 NULL, (caddr_t *)&mem->va, &len, &mem->idm_acc_handle);
82 if (rc != DDI_SUCCESS) {
83 mem->idm_acc_handle = NULL;
84 mem->va = NULL;
85 ASSERT(mem->idm_dma_handle != NULL);
86 ddi_dma_free_handle(&mem->idm_dma_handle);
87 mem->idm_dma_handle = NULL;
88
89 i40e_error(i40e, "failed to allocate %" PRIu64 " bytes of DMA "
90 "memory for common code", size);
91 return (I40E_ERR_NO_MEMORY);
92 }
93
94 bzero(mem->va, len);
95
96 rc = ddi_dma_addr_bind_handle(mem->idm_dma_handle, NULL, mem->va, len,
97 DDI_DMA_RDWR | DDI_DMA_STREAMING, DDI_DMA_DONTWAIT, NULL,
98 &cookie, &cookie_num);
99 if (rc != DDI_DMA_MAPPED) {
100 mem->pa = NULL;
101 ASSERT(mem->idm_acc_handle != NULL);
102 ddi_dma_mem_free(&mem->idm_acc_handle);
103 mem->idm_acc_handle = NULL;
104 mem->va = NULL;
105 ASSERT(mem->idm_dma_handle != NULL);
106 ddi_dma_free_handle(&mem->idm_dma_handle);
107 mem->idm_dma_handle = NULL;
108
109 i40e_error(i40e, "failed to bind %ld byte sized dma region: %d",
110 len, rc);
111 switch (rc) {
112 case DDI_DMA_INUSE:
113 return (I40E_ERR_NOT_READY);
114 case DDI_DMA_TOOBIG:
115 return (I40E_ERR_INVALID_SIZE);
116 case DDI_DMA_NOMAPPING:
117 case DDI_DMA_NORESOURCES:
118 default:
119 return (I40E_ERR_NO_MEMORY);
120 }
121 }
122
123 ASSERT(cookie_num == 1);
124 mem->pa = cookie.dmac_laddress;
125 /*
126 * Lint doesn't like this because the common code gives us a uint64_t as
127 * input, but the common code then asks us to assign it to a size_t. So
128 * lint's right, but in this case there isn't much we can do.
129 */
130 mem->size = (size_t)size;
131
132 return (I40E_SUCCESS);
133 }
134
135 /* ARGSUSED */
136 i40e_status
i40e_free_dma_mem(struct i40e_hw * hw,struct i40e_dma_mem * mem)137 i40e_free_dma_mem(struct i40e_hw *hw, struct i40e_dma_mem *mem)
138 {
139 if (mem->pa != 0) {
140 VERIFY(mem->idm_dma_handle != NULL);
141 (void) ddi_dma_unbind_handle(mem->idm_dma_handle);
142 mem->pa = 0;
143 mem->size = 0;
144 }
145
146 if (mem->idm_acc_handle != NULL) {
147 ddi_dma_mem_free(&mem->idm_acc_handle);
148 mem->idm_acc_handle = NULL;
149 mem->va = NULL;
150 }
151
152 if (mem->idm_dma_handle != NULL) {
153 ddi_dma_free_handle(&mem->idm_dma_handle);
154 mem->idm_dma_handle = NULL;
155 }
156
157 /*
158 * Watch out for sloppiness.
159 */
160 ASSERT(mem->pa == 0);
161 ASSERT(mem->va == NULL);
162 ASSERT(mem->size == 0);
163 mem->idm_alignment = UINT32_MAX;
164
165 return (I40E_SUCCESS);
166 }
167
168 /*
169 * The common code wants to initialize its 'spinlocks' here, aka adaptive
170 * mutexes. At this time these are only used to maintain the adminq's data and
171 * as such it will only be used outside of interrupt context and even then,
172 * we're not going to actually end up ever doing anything above lock level and
173 * up in doing stuff with high level interrupts.
174 */
175 void
i40e_init_spinlock(struct i40e_spinlock * lock)176 i40e_init_spinlock(struct i40e_spinlock *lock)
177 {
178 mutex_init(&lock->ispl_mutex, NULL, MUTEX_DRIVER, NULL);
179 }
180
181 void
i40e_acquire_spinlock(struct i40e_spinlock * lock)182 i40e_acquire_spinlock(struct i40e_spinlock *lock)
183 {
184 mutex_enter(&lock->ispl_mutex);
185 }
186
187 void
i40e_release_spinlock(struct i40e_spinlock * lock)188 i40e_release_spinlock(struct i40e_spinlock *lock)
189 {
190 mutex_exit(&lock->ispl_mutex);
191 }
192
193 void
i40e_destroy_spinlock(struct i40e_spinlock * lock)194 i40e_destroy_spinlock(struct i40e_spinlock *lock)
195 {
196 mutex_destroy(&lock->ispl_mutex);
197 }
198
199 boolean_t
i40e_set_hw_bus_info(struct i40e_hw * hw)200 i40e_set_hw_bus_info(struct i40e_hw *hw)
201 {
202 uint8_t pcie_id = PCI_CAP_ID_PCI_E;
203 uint16_t pcie_cap, value;
204 int status;
205
206 /* locate the pci-e capability block */
207 status = pci_lcap_locate((OS_DEP(hw))->ios_cfg_handle, pcie_id,
208 &pcie_cap);
209 if (status != DDI_SUCCESS) {
210 i40e_error(OS_DEP(hw)->ios_i40e, "failed to locate PCIe "
211 "capability block: %d",
212 status);
213 return (B_FALSE);
214 }
215
216 value = pci_config_get16(OS_DEP(hw)->ios_cfg_handle,
217 pcie_cap + PCIE_LINKSTS);
218
219 i40e_set_pci_config_data(hw, value);
220
221 return (B_TRUE);
222 }
223
224 /* ARGSUSED */
225 void
i40e_debug(void * hw,u32 mask,char * fmt,...)226 i40e_debug(void *hw, u32 mask, char *fmt, ...)
227 {
228 char buf[1024];
229 va_list args;
230
231 va_start(args, fmt);
232 (void) vsnprintf(buf, sizeof (buf), fmt, args);
233 va_end(args);
234
235 DTRACE_PROBE2(i40e__debug, uint32_t, mask, char *, buf);
236 }
237