Commit | Line | Data |
---|---|---|
fe56b9e6 YM |
1 | /* QLogic qed NIC Driver |
2 | * Copyright (c) 2015 QLogic Corporation | |
3 | * | |
4 | * This software is available under the terms of the GNU General Public License | |
5 | * (GPL) Version 2, available from the file COPYING in the main directory of | |
6 | * this source tree. | |
7 | */ | |
8 | ||
9 | #ifndef _QED_CHAIN_H | |
10 | #define _QED_CHAIN_H | |
11 | ||
12 | #include <linux/types.h> | |
13 | #include <asm/byteorder.h> | |
14 | #include <linux/kernel.h> | |
15 | #include <linux/list.h> | |
16 | #include <linux/slab.h> | |
17 | #include <linux/qed/common_hsi.h> | |
18 | ||
19 | /* dma_addr_t manip */ | |
20 | #define DMA_LO_LE(x) cpu_to_le32(lower_32_bits(x)) | |
21 | #define DMA_HI_LE(x) cpu_to_le32(upper_32_bits(x)) | |
94494598 YM |
22 | #define DMA_REGPAIR_LE(x, val) do { \ |
23 | (x).hi = DMA_HI_LE((val)); \ | |
24 | (x).lo = DMA_LO_LE((val)); \ | |
25 | } while (0) | |
fe56b9e6 YM |
26 | |
27 | #define HILO_GEN(hi, lo, type) ((((type)(hi)) << 32) + (lo)) | |
fe56b9e6 | 28 | #define HILO_64(hi, lo) HILO_GEN((le32_to_cpu(hi)), (le32_to_cpu(lo)), u64) |
fe56b9e6 | 29 | #define HILO_64_REGPAIR(regpair) (HILO_64(regpair.hi, regpair.lo)) |
81b1251d | 30 | #define HILO_DMA_REGPAIR(regpair) ((dma_addr_t)HILO_64_REGPAIR(regpair)) |
fe56b9e6 YM |
31 | |
32 | enum qed_chain_mode { | |
33 | /* Each Page contains a next pointer at its end */ | |
34 | QED_CHAIN_MODE_NEXT_PTR, | |
35 | ||
36 | /* Chain is a single page (next ptr) is unrequired */ | |
37 | QED_CHAIN_MODE_SINGLE, | |
38 | ||
39 | /* Page pointers are located in a side list */ | |
40 | QED_CHAIN_MODE_PBL, | |
41 | }; | |
42 | ||
43 | enum qed_chain_use_mode { | |
44 | QED_CHAIN_USE_TO_PRODUCE, /* Chain starts empty */ | |
45 | QED_CHAIN_USE_TO_CONSUME, /* Chain starts full */ | |
46 | QED_CHAIN_USE_TO_CONSUME_PRODUCE, /* Chain starts empty */ | |
47 | }; | |
48 | ||
a91eb52a YM |
49 | enum qed_chain_cnt_type { |
50 | /* The chain's size/prod/cons are kept in 16-bit variables */ | |
51 | QED_CHAIN_CNT_TYPE_U16, | |
52 | ||
53 | /* The chain's size/prod/cons are kept in 32-bit variables */ | |
54 | QED_CHAIN_CNT_TYPE_U32, | |
55 | }; | |
56 | ||
fe56b9e6 YM |
57 | struct qed_chain_next { |
58 | struct regpair next_phys; | |
59 | void *next_virt; | |
60 | }; | |
61 | ||
a91eb52a YM |
62 | struct qed_chain_pbl_u16 { |
63 | u16 prod_page_idx; | |
64 | u16 cons_page_idx; | |
65 | }; | |
66 | ||
67 | struct qed_chain_pbl_u32 { | |
68 | u32 prod_page_idx; | |
69 | u32 cons_page_idx; | |
70 | }; | |
71 | ||
fe56b9e6 | 72 | struct qed_chain_pbl { |
a91eb52a | 73 | /* Base address of a pre-allocated buffer for pbl */ |
fe56b9e6 YM |
74 | dma_addr_t p_phys_table; |
75 | void *p_virt_table; | |
a91eb52a YM |
76 | |
77 | /* Table for keeping the virtual addresses of the chain pages, | |
78 | * respectively to the physical addresses in the pbl table. | |
79 | */ | |
80 | void **pp_virt_addr_tbl; | |
81 | ||
82 | /* Index to current used page by producer/consumer */ | |
83 | union { | |
84 | struct qed_chain_pbl_u16 pbl16; | |
85 | struct qed_chain_pbl_u32 pbl32; | |
86 | } u; | |
87 | }; | |
88 | ||
89 | struct qed_chain_u16 { | |
90 | /* Cyclic index of next element to produce/consme */ | |
91 | u16 prod_idx; | |
92 | u16 cons_idx; | |
93 | }; | |
94 | ||
95 | struct qed_chain_u32 { | |
96 | /* Cyclic index of next element to produce/consme */ | |
97 | u32 prod_idx; | |
98 | u32 cons_idx; | |
fe56b9e6 YM |
99 | }; |
100 | ||
101 | struct qed_chain { | |
102 | void *p_virt_addr; | |
103 | dma_addr_t p_phys_addr; | |
104 | void *p_prod_elem; | |
105 | void *p_cons_elem; | |
a91eb52a | 106 | |
fe56b9e6 YM |
107 | enum qed_chain_mode mode; |
108 | enum qed_chain_use_mode intended_use; /* used to produce/consume */ | |
a91eb52a YM |
109 | enum qed_chain_cnt_type cnt_type; |
110 | ||
111 | union { | |
112 | struct qed_chain_u16 chain16; | |
113 | struct qed_chain_u32 chain32; | |
114 | } u; | |
115 | ||
116 | u32 page_cnt; | |
117 | ||
118 | /* Number of elements - capacity is for usable elements only, | |
119 | * while size will contain total number of elements [for entire chain]. | |
120 | */ | |
121 | u32 capacity; | |
122 | u32 size; | |
123 | ||
124 | /* Elements information for fast calculations */ | |
fe56b9e6 YM |
125 | u16 elem_per_page; |
126 | u16 elem_per_page_mask; | |
127 | u16 elem_unusable; | |
128 | u16 usable_per_page; | |
129 | u16 elem_size; | |
130 | u16 next_page_mask; | |
131 | struct qed_chain_pbl pbl; | |
132 | }; | |
133 | ||
134 | #define QED_CHAIN_PBL_ENTRY_SIZE (8) | |
135 | #define QED_CHAIN_PAGE_SIZE (0x1000) | |
136 | #define ELEMS_PER_PAGE(elem_size) (QED_CHAIN_PAGE_SIZE / (elem_size)) | |
137 | ||
138 | #define UNUSABLE_ELEMS_PER_PAGE(elem_size, mode) \ | |
139 | ((mode == QED_CHAIN_MODE_NEXT_PTR) ? \ | |
140 | (1 + ((sizeof(struct qed_chain_next) - 1) / \ | |
141 | (elem_size))) : 0) | |
142 | ||
143 | #define USABLE_ELEMS_PER_PAGE(elem_size, mode) \ | |
144 | ((u32)(ELEMS_PER_PAGE(elem_size) - \ | |
145 | UNUSABLE_ELEMS_PER_PAGE(elem_size, mode))) | |
146 | ||
147 | #define QED_CHAIN_PAGE_CNT(elem_cnt, elem_size, mode) \ | |
148 | DIV_ROUND_UP(elem_cnt, USABLE_ELEMS_PER_PAGE(elem_size, mode)) | |
149 | ||
a91eb52a YM |
150 | #define is_chain_u16(p) ((p)->cnt_type == QED_CHAIN_CNT_TYPE_U16) |
151 | #define is_chain_u32(p) ((p)->cnt_type == QED_CHAIN_CNT_TYPE_U32) | |
152 | ||
fe56b9e6 YM |
153 | /* Accessors */ |
154 | static inline u16 qed_chain_get_prod_idx(struct qed_chain *p_chain) | |
155 | { | |
a91eb52a | 156 | return p_chain->u.chain16.prod_idx; |
fe56b9e6 YM |
157 | } |
158 | ||
159 | static inline u16 qed_chain_get_cons_idx(struct qed_chain *p_chain) | |
160 | { | |
a91eb52a YM |
161 | return p_chain->u.chain16.cons_idx; |
162 | } | |
163 | ||
164 | static inline u32 qed_chain_get_cons_idx_u32(struct qed_chain *p_chain) | |
165 | { | |
166 | return p_chain->u.chain32.cons_idx; | |
fe56b9e6 YM |
167 | } |
168 | ||
169 | static inline u16 qed_chain_get_elem_left(struct qed_chain *p_chain) | |
170 | { | |
171 | u16 used; | |
172 | ||
a91eb52a YM |
173 | used = (u16) (((u32)0x10000 + |
174 | (u32)p_chain->u.chain16.prod_idx) - | |
175 | (u32)p_chain->u.chain16.cons_idx); | |
fe56b9e6 | 176 | if (p_chain->mode == QED_CHAIN_MODE_NEXT_PTR) |
a91eb52a YM |
177 | used -= p_chain->u.chain16.prod_idx / p_chain->elem_per_page - |
178 | p_chain->u.chain16.cons_idx / p_chain->elem_per_page; | |
fe56b9e6 | 179 | |
a91eb52a | 180 | return (u16)(p_chain->capacity - used); |
fe56b9e6 YM |
181 | } |
182 | ||
a91eb52a | 183 | static inline u32 qed_chain_get_elem_left_u32(struct qed_chain *p_chain) |
fe56b9e6 | 184 | { |
a91eb52a | 185 | u32 used; |
fe56b9e6 | 186 | |
a91eb52a YM |
187 | used = (u32) (((u64)0x100000000ULL + |
188 | (u64)p_chain->u.chain32.prod_idx) - | |
189 | (u64)p_chain->u.chain32.cons_idx); | |
190 | if (p_chain->mode == QED_CHAIN_MODE_NEXT_PTR) | |
191 | used -= p_chain->u.chain32.prod_idx / p_chain->elem_per_page - | |
192 | p_chain->u.chain32.cons_idx / p_chain->elem_per_page; | |
fe56b9e6 | 193 | |
a91eb52a | 194 | return p_chain->capacity - used; |
fe56b9e6 YM |
195 | } |
196 | ||
a91eb52a | 197 | static inline u16 qed_chain_get_usable_per_page(struct qed_chain *p_chain) |
fe56b9e6 YM |
198 | { |
199 | return p_chain->usable_per_page; | |
200 | } | |
201 | ||
a91eb52a | 202 | static inline u16 qed_chain_get_unusable_per_page(struct qed_chain *p_chain) |
fe56b9e6 YM |
203 | { |
204 | return p_chain->elem_unusable; | |
205 | } | |
206 | ||
a91eb52a | 207 | static inline u32 qed_chain_get_page_cnt(struct qed_chain *p_chain) |
fe56b9e6 | 208 | { |
a91eb52a | 209 | return p_chain->page_cnt; |
fe56b9e6 YM |
210 | } |
211 | ||
a91eb52a | 212 | static inline dma_addr_t qed_chain_get_pbl_phys(struct qed_chain *p_chain) |
fe56b9e6 YM |
213 | { |
214 | return p_chain->pbl.p_phys_table; | |
215 | } | |
216 | ||
217 | /** | |
218 | * @brief qed_chain_advance_page - | |
219 | * | |
220 | * Advance the next element accros pages for a linked chain | |
221 | * | |
222 | * @param p_chain | |
223 | * @param p_next_elem | |
224 | * @param idx_to_inc | |
225 | * @param page_to_inc | |
226 | */ | |
227 | static inline void | |
228 | qed_chain_advance_page(struct qed_chain *p_chain, | |
a91eb52a | 229 | void **p_next_elem, void *idx_to_inc, void *page_to_inc) |
fe56b9e6 YM |
230 | |
231 | { | |
a91eb52a YM |
232 | struct qed_chain_next *p_next = NULL; |
233 | u32 page_index = 0; | |
fe56b9e6 YM |
234 | switch (p_chain->mode) { |
235 | case QED_CHAIN_MODE_NEXT_PTR: | |
a91eb52a | 236 | p_next = *p_next_elem; |
fe56b9e6 | 237 | *p_next_elem = p_next->next_virt; |
a91eb52a YM |
238 | if (is_chain_u16(p_chain)) |
239 | *(u16 *)idx_to_inc += p_chain->elem_unusable; | |
240 | else | |
241 | *(u32 *)idx_to_inc += p_chain->elem_unusable; | |
fe56b9e6 | 242 | break; |
fe56b9e6 YM |
243 | case QED_CHAIN_MODE_SINGLE: |
244 | *p_next_elem = p_chain->p_virt_addr; | |
245 | break; | |
246 | ||
247 | case QED_CHAIN_MODE_PBL: | |
a91eb52a YM |
248 | if (is_chain_u16(p_chain)) { |
249 | if (++(*(u16 *)page_to_inc) == p_chain->page_cnt) | |
250 | *(u16 *)page_to_inc = 0; | |
251 | page_index = *(u16 *)page_to_inc; | |
252 | } else { | |
253 | if (++(*(u32 *)page_to_inc) == p_chain->page_cnt) | |
254 | *(u32 *)page_to_inc = 0; | |
255 | page_index = *(u32 *)page_to_inc; | |
fe56b9e6 | 256 | } |
a91eb52a | 257 | *p_next_elem = p_chain->pbl.pp_virt_addr_tbl[page_index]; |
fe56b9e6 YM |
258 | } |
259 | } | |
260 | ||
261 | #define is_unusable_idx(p, idx) \ | |
a91eb52a YM |
262 | (((p)->u.chain16.idx & (p)->elem_per_page_mask) == (p)->usable_per_page) |
263 | ||
264 | #define is_unusable_idx_u32(p, idx) \ | |
265 | (((p)->u.chain32.idx & (p)->elem_per_page_mask) == (p)->usable_per_page) | |
266 | #define is_unusable_next_idx(p, idx) \ | |
267 | ((((p)->u.chain16.idx + 1) & (p)->elem_per_page_mask) == \ | |
268 | (p)->usable_per_page) | |
fe56b9e6 | 269 | |
a91eb52a YM |
270 | #define is_unusable_next_idx_u32(p, idx) \ |
271 | ((((p)->u.chain32.idx + 1) & (p)->elem_per_page_mask) == \ | |
272 | (p)->usable_per_page) | |
fe56b9e6 | 273 | |
a91eb52a | 274 | #define test_and_skip(p, idx) \ |
fe56b9e6 | 275 | do { \ |
a91eb52a YM |
276 | if (is_chain_u16(p)) { \ |
277 | if (is_unusable_idx(p, idx)) \ | |
278 | (p)->u.chain16.idx += (p)->elem_unusable; \ | |
279 | } else { \ | |
280 | if (is_unusable_idx_u32(p, idx)) \ | |
281 | (p)->u.chain32.idx += (p)->elem_unusable; \ | |
fe56b9e6 YM |
282 | } \ |
283 | } while (0) | |
284 | ||
fe56b9e6 YM |
285 | /** |
286 | * @brief qed_chain_return_produced - | |
287 | * | |
288 | * A chain in which the driver "Produces" elements should use this API | |
289 | * to indicate previous produced elements are now consumed. | |
290 | * | |
291 | * @param p_chain | |
292 | */ | |
293 | static inline void qed_chain_return_produced(struct qed_chain *p_chain) | |
294 | { | |
a91eb52a YM |
295 | if (is_chain_u16(p_chain)) |
296 | p_chain->u.chain16.cons_idx++; | |
297 | else | |
298 | p_chain->u.chain32.cons_idx++; | |
299 | test_and_skip(p_chain, cons_idx); | |
fe56b9e6 YM |
300 | } |
301 | ||
302 | /** | |
303 | * @brief qed_chain_produce - | |
304 | * | |
305 | * A chain in which the driver "Produces" elements should use this to get | |
306 | * a pointer to the next element which can be "Produced". It's driver | |
307 | * responsibility to validate that the chain has room for new element. | |
308 | * | |
309 | * @param p_chain | |
310 | * | |
311 | * @return void*, a pointer to next element | |
312 | */ | |
313 | static inline void *qed_chain_produce(struct qed_chain *p_chain) | |
314 | { | |
a91eb52a YM |
315 | void *p_ret = NULL, *p_prod_idx, *p_prod_page_idx; |
316 | ||
317 | if (is_chain_u16(p_chain)) { | |
318 | if ((p_chain->u.chain16.prod_idx & | |
319 | p_chain->elem_per_page_mask) == p_chain->next_page_mask) { | |
320 | p_prod_idx = &p_chain->u.chain16.prod_idx; | |
321 | p_prod_page_idx = &p_chain->pbl.u.pbl16.prod_page_idx; | |
322 | qed_chain_advance_page(p_chain, &p_chain->p_prod_elem, | |
323 | p_prod_idx, p_prod_page_idx); | |
324 | } | |
325 | p_chain->u.chain16.prod_idx++; | |
326 | } else { | |
327 | if ((p_chain->u.chain32.prod_idx & | |
328 | p_chain->elem_per_page_mask) == p_chain->next_page_mask) { | |
329 | p_prod_idx = &p_chain->u.chain32.prod_idx; | |
330 | p_prod_page_idx = &p_chain->pbl.u.pbl32.prod_page_idx; | |
331 | qed_chain_advance_page(p_chain, &p_chain->p_prod_elem, | |
332 | p_prod_idx, p_prod_page_idx); | |
333 | } | |
334 | p_chain->u.chain32.prod_idx++; | |
fe56b9e6 YM |
335 | } |
336 | ||
a91eb52a | 337 | p_ret = p_chain->p_prod_elem; |
fe56b9e6 YM |
338 | p_chain->p_prod_elem = (void *)(((u8 *)p_chain->p_prod_elem) + |
339 | p_chain->elem_size); | |
340 | ||
a91eb52a | 341 | return p_ret; |
fe56b9e6 YM |
342 | } |
343 | ||
344 | /** | |
345 | * @brief qed_chain_get_capacity - | |
346 | * | |
347 | * Get the maximum number of BDs in chain | |
348 | * | |
349 | * @param p_chain | |
350 | * @param num | |
351 | * | |
a91eb52a | 352 | * @return number of unusable BDs |
fe56b9e6 | 353 | */ |
a91eb52a | 354 | static inline u32 qed_chain_get_capacity(struct qed_chain *p_chain) |
fe56b9e6 YM |
355 | { |
356 | return p_chain->capacity; | |
357 | } | |
358 | ||
359 | /** | |
360 | * @brief qed_chain_recycle_consumed - | |
361 | * | |
362 | * Returns an element which was previously consumed; | |
363 | * Increments producers so they could be written to FW. | |
364 | * | |
365 | * @param p_chain | |
366 | */ | |
a91eb52a | 367 | static inline void qed_chain_recycle_consumed(struct qed_chain *p_chain) |
fe56b9e6 | 368 | { |
a91eb52a YM |
369 | test_and_skip(p_chain, prod_idx); |
370 | if (is_chain_u16(p_chain)) | |
371 | p_chain->u.chain16.prod_idx++; | |
372 | else | |
373 | p_chain->u.chain32.prod_idx++; | |
fe56b9e6 YM |
374 | } |
375 | ||
376 | /** | |
377 | * @brief qed_chain_consume - | |
378 | * | |
379 | * A Chain in which the driver utilizes data written by a different source | |
380 | * (i.e., FW) should use this to access passed buffers. | |
381 | * | |
382 | * @param p_chain | |
383 | * | |
384 | * @return void*, a pointer to the next buffer written | |
385 | */ | |
386 | static inline void *qed_chain_consume(struct qed_chain *p_chain) | |
387 | { | |
a91eb52a YM |
388 | void *p_ret = NULL, *p_cons_idx, *p_cons_page_idx; |
389 | ||
390 | if (is_chain_u16(p_chain)) { | |
391 | if ((p_chain->u.chain16.cons_idx & | |
392 | p_chain->elem_per_page_mask) == p_chain->next_page_mask) { | |
393 | p_cons_idx = &p_chain->u.chain16.cons_idx; | |
394 | p_cons_page_idx = &p_chain->pbl.u.pbl16.cons_page_idx; | |
395 | qed_chain_advance_page(p_chain, &p_chain->p_cons_elem, | |
396 | p_cons_idx, p_cons_page_idx); | |
397 | } | |
398 | p_chain->u.chain16.cons_idx++; | |
399 | } else { | |
400 | if ((p_chain->u.chain32.cons_idx & | |
401 | p_chain->elem_per_page_mask) == p_chain->next_page_mask) { | |
402 | p_cons_idx = &p_chain->u.chain32.cons_idx; | |
403 | p_cons_page_idx = &p_chain->pbl.u.pbl32.cons_page_idx; | |
fe56b9e6 | 404 | qed_chain_advance_page(p_chain, &p_chain->p_cons_elem, |
a91eb52a YM |
405 | p_cons_idx, p_cons_page_idx); |
406 | } | |
407 | p_chain->u.chain32.cons_idx++; | |
fe56b9e6 YM |
408 | } |
409 | ||
a91eb52a | 410 | p_ret = p_chain->p_cons_elem; |
fe56b9e6 YM |
411 | p_chain->p_cons_elem = (void *)(((u8 *)p_chain->p_cons_elem) + |
412 | p_chain->elem_size); | |
413 | ||
a91eb52a | 414 | return p_ret; |
fe56b9e6 YM |
415 | } |
416 | ||
417 | /** | |
418 | * @brief qed_chain_reset - Resets the chain to its start state | |
419 | * | |
420 | * @param p_chain pointer to a previously allocted chain | |
421 | */ | |
422 | static inline void qed_chain_reset(struct qed_chain *p_chain) | |
423 | { | |
a91eb52a YM |
424 | u32 i; |
425 | ||
426 | if (is_chain_u16(p_chain)) { | |
427 | p_chain->u.chain16.prod_idx = 0; | |
428 | p_chain->u.chain16.cons_idx = 0; | |
429 | } else { | |
430 | p_chain->u.chain32.prod_idx = 0; | |
431 | p_chain->u.chain32.cons_idx = 0; | |
432 | } | |
433 | p_chain->p_cons_elem = p_chain->p_virt_addr; | |
434 | p_chain->p_prod_elem = p_chain->p_virt_addr; | |
fe56b9e6 YM |
435 | |
436 | if (p_chain->mode == QED_CHAIN_MODE_PBL) { | |
a91eb52a YM |
437 | /* Use (page_cnt - 1) as a reset value for the prod/cons page's |
438 | * indices, to avoid unnecessary page advancing on the first | |
439 | * call to qed_chain_produce/consume. Instead, the indices | |
440 | * will be advanced to page_cnt and then will be wrapped to 0. | |
441 | */ | |
442 | u32 reset_val = p_chain->page_cnt - 1; | |
443 | ||
444 | if (is_chain_u16(p_chain)) { | |
445 | p_chain->pbl.u.pbl16.prod_page_idx = (u16)reset_val; | |
446 | p_chain->pbl.u.pbl16.cons_page_idx = (u16)reset_val; | |
447 | } else { | |
448 | p_chain->pbl.u.pbl32.prod_page_idx = reset_val; | |
449 | p_chain->pbl.u.pbl32.cons_page_idx = reset_val; | |
450 | } | |
fe56b9e6 YM |
451 | } |
452 | ||
453 | switch (p_chain->intended_use) { | |
454 | case QED_CHAIN_USE_TO_CONSUME_PRODUCE: | |
455 | case QED_CHAIN_USE_TO_PRODUCE: | |
456 | /* Do nothing */ | |
457 | break; | |
458 | ||
459 | case QED_CHAIN_USE_TO_CONSUME: | |
460 | /* produce empty elements */ | |
461 | for (i = 0; i < p_chain->capacity; i++) | |
462 | qed_chain_recycle_consumed(p_chain); | |
463 | break; | |
464 | } | |
465 | } | |
466 | ||
467 | /** | |
468 | * @brief qed_chain_init - Initalizes a basic chain struct | |
469 | * | |
470 | * @param p_chain | |
471 | * @param p_virt_addr | |
472 | * @param p_phys_addr physical address of allocated buffer's beginning | |
473 | * @param page_cnt number of pages in the allocated buffer | |
474 | * @param elem_size size of each element in the chain | |
475 | * @param intended_use | |
476 | * @param mode | |
477 | */ | |
a91eb52a YM |
478 | static inline void qed_chain_init_params(struct qed_chain *p_chain, |
479 | u32 page_cnt, | |
480 | u8 elem_size, | |
481 | enum qed_chain_use_mode intended_use, | |
482 | enum qed_chain_mode mode, | |
483 | enum qed_chain_cnt_type cnt_type) | |
fe56b9e6 YM |
484 | { |
485 | /* chain fixed parameters */ | |
a91eb52a YM |
486 | p_chain->p_virt_addr = NULL; |
487 | p_chain->p_phys_addr = 0; | |
fe56b9e6 | 488 | p_chain->elem_size = elem_size; |
a91eb52a | 489 | p_chain->intended_use = intended_use; |
fe56b9e6 | 490 | p_chain->mode = mode; |
a91eb52a | 491 | p_chain->cnt_type = cnt_type; |
fe56b9e6 | 492 | |
fe56b9e6 | 493 | p_chain->elem_per_page = ELEMS_PER_PAGE(elem_size); |
a91eb52a | 494 | p_chain->usable_per_page = USABLE_ELEMS_PER_PAGE(elem_size, mode); |
fe56b9e6 | 495 | p_chain->elem_per_page_mask = p_chain->elem_per_page - 1; |
fe56b9e6 | 496 | p_chain->elem_unusable = UNUSABLE_ELEMS_PER_PAGE(elem_size, mode); |
fe56b9e6 YM |
497 | p_chain->next_page_mask = (p_chain->usable_per_page & |
498 | p_chain->elem_per_page_mask); | |
499 | ||
a91eb52a YM |
500 | p_chain->page_cnt = page_cnt; |
501 | p_chain->capacity = p_chain->usable_per_page * page_cnt; | |
502 | p_chain->size = p_chain->elem_per_page * page_cnt; | |
fe56b9e6 | 503 | |
a91eb52a YM |
504 | p_chain->pbl.p_phys_table = 0; |
505 | p_chain->pbl.p_virt_table = NULL; | |
506 | p_chain->pbl.pp_virt_addr_tbl = NULL; | |
fe56b9e6 YM |
507 | } |
508 | ||
509 | /** | |
a91eb52a YM |
510 | * @brief qed_chain_init_mem - |
511 | * | |
512 | * Initalizes a basic chain struct with its chain buffers | |
513 | * | |
fe56b9e6 YM |
514 | * @param p_chain |
515 | * @param p_virt_addr virtual address of allocated buffer's beginning | |
516 | * @param p_phys_addr physical address of allocated buffer's beginning | |
a91eb52a | 517 | * |
fe56b9e6 | 518 | */ |
a91eb52a YM |
519 | static inline void qed_chain_init_mem(struct qed_chain *p_chain, |
520 | void *p_virt_addr, dma_addr_t p_phys_addr) | |
fe56b9e6 | 521 | { |
a91eb52a YM |
522 | p_chain->p_virt_addr = p_virt_addr; |
523 | p_chain->p_phys_addr = p_phys_addr; | |
524 | } | |
fe56b9e6 | 525 | |
a91eb52a YM |
526 | /** |
527 | * @brief qed_chain_init_pbl_mem - | |
528 | * | |
529 | * Initalizes a basic chain struct with its pbl buffers | |
530 | * | |
531 | * @param p_chain | |
532 | * @param p_virt_pbl pointer to a pre allocated side table which will hold | |
533 | * virtual page addresses. | |
534 | * @param p_phys_pbl pointer to a pre-allocated side table which will hold | |
535 | * physical page addresses. | |
536 | * @param pp_virt_addr_tbl | |
537 | * pointer to a pre-allocated side table which will hold | |
538 | * the virtual addresses of the chain pages. | |
539 | * | |
540 | */ | |
541 | static inline void qed_chain_init_pbl_mem(struct qed_chain *p_chain, | |
542 | void *p_virt_pbl, | |
543 | dma_addr_t p_phys_pbl, | |
544 | void **pp_virt_addr_tbl) | |
545 | { | |
fe56b9e6 YM |
546 | p_chain->pbl.p_phys_table = p_phys_pbl; |
547 | p_chain->pbl.p_virt_table = p_virt_pbl; | |
a91eb52a | 548 | p_chain->pbl.pp_virt_addr_tbl = pp_virt_addr_tbl; |
fe56b9e6 YM |
549 | } |
550 | ||
551 | /** | |
a91eb52a YM |
552 | * @brief qed_chain_init_next_ptr_elem - |
553 | * | |
554 | * Initalizes a next pointer element | |
555 | * | |
556 | * @param p_chain | |
557 | * @param p_virt_curr virtual address of a chain page of which the next | |
558 | * pointer element is initialized | |
559 | * @param p_virt_next virtual address of the next chain page | |
560 | * @param p_phys_next physical address of the next chain page | |
fe56b9e6 | 561 | * |
fe56b9e6 | 562 | */ |
a91eb52a YM |
563 | static inline void |
564 | qed_chain_init_next_ptr_elem(struct qed_chain *p_chain, | |
565 | void *p_virt_curr, | |
566 | void *p_virt_next, dma_addr_t p_phys_next) | |
fe56b9e6 | 567 | { |
a91eb52a YM |
568 | struct qed_chain_next *p_next; |
569 | u32 size; | |
570 | ||
571 | size = p_chain->elem_size * p_chain->usable_per_page; | |
572 | p_next = (struct qed_chain_next *)((u8 *)p_virt_curr + size); | |
573 | ||
574 | DMA_REGPAIR_LE(p_next->next_phys, p_phys_next); | |
575 | ||
576 | p_next->next_virt = p_virt_next; | |
fe56b9e6 YM |
577 | } |
578 | ||
579 | /** | |
a91eb52a | 580 | * @brief qed_chain_get_last_elem - |
fe56b9e6 | 581 | * |
a91eb52a | 582 | * Returns a pointer to the last element of the chain |
fe56b9e6 YM |
583 | * |
584 | * @param p_chain | |
fe56b9e6 | 585 | * |
a91eb52a | 586 | * @return void* |
fe56b9e6 | 587 | */ |
a91eb52a | 588 | static inline void *qed_chain_get_last_elem(struct qed_chain *p_chain) |
fe56b9e6 | 589 | { |
a91eb52a YM |
590 | struct qed_chain_next *p_next = NULL; |
591 | void *p_virt_addr = NULL; | |
592 | u32 size, last_page_idx; | |
fe56b9e6 | 593 | |
a91eb52a YM |
594 | if (!p_chain->p_virt_addr) |
595 | goto out; | |
fe56b9e6 | 596 | |
a91eb52a YM |
597 | switch (p_chain->mode) { |
598 | case QED_CHAIN_MODE_NEXT_PTR: | |
599 | size = p_chain->elem_size * p_chain->usable_per_page; | |
600 | p_virt_addr = p_chain->p_virt_addr; | |
601 | p_next = (struct qed_chain_next *)((u8 *)p_virt_addr + size); | |
602 | while (p_next->next_virt != p_chain->p_virt_addr) { | |
603 | p_virt_addr = p_next->next_virt; | |
604 | p_next = (struct qed_chain_next *)((u8 *)p_virt_addr + | |
605 | size); | |
606 | } | |
607 | break; | |
608 | case QED_CHAIN_MODE_SINGLE: | |
609 | p_virt_addr = p_chain->p_virt_addr; | |
610 | break; | |
611 | case QED_CHAIN_MODE_PBL: | |
612 | last_page_idx = p_chain->page_cnt - 1; | |
613 | p_virt_addr = p_chain->pbl.pp_virt_addr_tbl[last_page_idx]; | |
614 | break; | |
615 | } | |
616 | /* p_virt_addr points at this stage to the last page of the chain */ | |
617 | size = p_chain->elem_size * (p_chain->usable_per_page - 1); | |
618 | p_virt_addr = (u8 *)p_virt_addr + size; | |
619 | out: | |
620 | return p_virt_addr; | |
fe56b9e6 YM |
621 | } |
622 | ||
623 | /** | |
a91eb52a | 624 | * @brief qed_chain_set_prod - sets the prod to the given value |
fe56b9e6 | 625 | * |
a91eb52a YM |
626 | * @param prod_idx |
627 | * @param p_prod_elem | |
628 | */ | |
629 | static inline void qed_chain_set_prod(struct qed_chain *p_chain, | |
630 | u32 prod_idx, void *p_prod_elem) | |
631 | { | |
632 | if (is_chain_u16(p_chain)) | |
633 | p_chain->u.chain16.prod_idx = (u16) prod_idx; | |
634 | else | |
635 | p_chain->u.chain32.prod_idx = prod_idx; | |
636 | p_chain->p_prod_elem = p_prod_elem; | |
637 | } | |
638 | ||
639 | /** | |
640 | * @brief qed_chain_pbl_zero_mem - set chain memory to 0 | |
fe56b9e6 YM |
641 | * |
642 | * @param p_chain | |
fe56b9e6 | 643 | */ |
a91eb52a | 644 | static inline void qed_chain_pbl_zero_mem(struct qed_chain *p_chain) |
fe56b9e6 | 645 | { |
a91eb52a YM |
646 | u32 i, page_cnt; |
647 | ||
648 | if (p_chain->mode != QED_CHAIN_MODE_PBL) | |
649 | return; | |
650 | ||
651 | page_cnt = qed_chain_get_page_cnt(p_chain); | |
652 | ||
653 | for (i = 0; i < page_cnt; i++) | |
654 | memset(p_chain->pbl.pp_virt_addr_tbl[i], 0, | |
655 | QED_CHAIN_PAGE_SIZE); | |
fe56b9e6 YM |
656 | } |
657 | ||
658 | #endif |