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
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
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
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 = 0;
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
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
176 i40e_init_spinlock(struct i40e_spinlock *lock)
177 {
178 	mutex_init(&lock->ispl_mutex, NULL, MUTEX_DRIVER, NULL);
179 }
180 
181 void
182 i40e_acquire_spinlock(struct i40e_spinlock *lock)
183 {
184 	mutex_enter(&lock->ispl_mutex);
185 }
186 
187 void
188 i40e_release_spinlock(struct i40e_spinlock *lock)
189 {
190 	mutex_exit(&lock->ispl_mutex);
191 }
192 
193 void
194 i40e_destroy_spinlock(struct i40e_spinlock *lock)
195 {
196 	mutex_destroy(&lock->ispl_mutex);
197 }
198 
199 boolean_t
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
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