1 /* 2 * Copyright (c) 2017-2018 Cavium, Inc. 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 7 * are met: 8 * 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 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 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 19 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 20 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 21 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 22 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 23 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 24 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 25 * POSSIBILITY OF SUCH DAMAGE. 26 * 27 * $FreeBSD$ 28 * 29 */ 30 31 #ifndef __ECORE_CHAIN_H__ 32 #define __ECORE_CHAIN_H__ 33 34 #include "common_hsi.h" 35 #include "ecore_utils.h" 36 37 enum ecore_chain_mode 38 { 39 /* Each Page contains a next pointer at its end */ 40 ECORE_CHAIN_MODE_NEXT_PTR, 41 42 /* Chain is a single page (next ptr) is unrequired */ 43 ECORE_CHAIN_MODE_SINGLE, 44 45 /* Page pointers are located in a side list */ 46 ECORE_CHAIN_MODE_PBL, 47 }; 48 49 enum ecore_chain_use_mode 50 { 51 ECORE_CHAIN_USE_TO_PRODUCE, /* Chain starts empty */ 52 ECORE_CHAIN_USE_TO_CONSUME, /* Chain starts full */ 53 ECORE_CHAIN_USE_TO_CONSUME_PRODUCE, /* Chain starts empty */ 54 }; 55 56 enum ecore_chain_cnt_type { 57 /* The chain's size/prod/cons are kept in 16-bit variables */ 58 ECORE_CHAIN_CNT_TYPE_U16, 59 60 /* The chain's size/prod/cons are kept in 32-bit variables */ 61 ECORE_CHAIN_CNT_TYPE_U32, 62 }; 63 64 struct ecore_chain_next 65 { 66 struct regpair next_phys; 67 void *next_virt; 68 }; 69 70 struct ecore_chain_pbl_u16 { 71 u16 prod_page_idx; 72 u16 cons_page_idx; 73 }; 74 75 struct ecore_chain_pbl_u32 { 76 u32 prod_page_idx; 77 u32 cons_page_idx; 78 }; 79 80 struct ecore_chain_ext_pbl 81 { 82 dma_addr_t p_pbl_phys; 83 void *p_pbl_virt; 84 }; 85 86 struct ecore_chain_u16 { 87 /* Cyclic index of next element to produce/consme */ 88 u16 prod_idx; 89 u16 cons_idx; 90 }; 91 92 struct ecore_chain_u32 { 93 /* Cyclic index of next element to produce/consme */ 94 u32 prod_idx; 95 u32 cons_idx; 96 }; 97 98 struct ecore_chain 99 { 100 /* fastpath portion of the chain - required for commands such 101 * as produce / consume. 102 */ 103 /* Point to next element to produce/consume */ 104 void *p_prod_elem; 105 void *p_cons_elem; 106 107 /* Fastpath portions of the PBL [if exists] */ 108 109 struct { 110 /* Table for keeping the virtual addresses of the chain pages, 111 * respectively to the physical addresses in the pbl table. 112 */ 113 void **pp_virt_addr_tbl; 114 115 union { 116 struct ecore_chain_pbl_u16 pbl_u16; 117 struct ecore_chain_pbl_u32 pbl_u32; 118 } c; 119 } pbl; 120 121 union { 122 struct ecore_chain_u16 chain16; 123 struct ecore_chain_u32 chain32; 124 } u; 125 126 /* Capacity counts only usable elements */ 127 u32 capacity; 128 u32 page_cnt; 129 130 /* A u8 would suffice for mode, but it would save as a lot of headaches 131 * on castings & defaults. 132 */ 133 enum ecore_chain_mode mode; 134 135 /* Elements information for fast calculations */ 136 u16 elem_per_page; 137 u16 elem_per_page_mask; 138 u16 elem_size; 139 u16 next_page_mask; 140 u16 usable_per_page; 141 u8 elem_unusable; 142 143 u8 cnt_type; 144 145 /* Slowpath of the chain - required for initialization and destruction, 146 * but isn't involved in regular functionality. 147 */ 148 149 /* Base address of a pre-allocated buffer for pbl */ 150 struct { 151 dma_addr_t p_phys_table; 152 void *p_virt_table; 153 } pbl_sp; 154 155 /* Address of first page of the chain - the address is required 156 * for fastpath operation [consume/produce] but only for the the SINGLE 157 * flavour which isn't considered fastpath [== SPQ]. 158 */ 159 void *p_virt_addr; 160 dma_addr_t p_phys_addr; 161 162 /* Total number of elements [for entire chain] */ 163 u32 size; 164 165 u8 intended_use; 166 167 /* TBD - do we really need this? Couldn't find usage for it */ 168 bool b_external_pbl; 169 170 void *dp_ctx; 171 }; 172 173 #define ECORE_CHAIN_PBL_ENTRY_SIZE (8) 174 #define ECORE_CHAIN_PAGE_SIZE (0x1000) 175 #define ELEMS_PER_PAGE(elem_size) (ECORE_CHAIN_PAGE_SIZE/(elem_size)) 176 177 #define UNUSABLE_ELEMS_PER_PAGE(elem_size, mode) \ 178 ((mode == ECORE_CHAIN_MODE_NEXT_PTR) ? \ 179 (u8)(1 + ((sizeof(struct ecore_chain_next)-1) / \ 180 (elem_size))) : 0) 181 182 #define USABLE_ELEMS_PER_PAGE(elem_size, mode) \ 183 ((u32) (ELEMS_PER_PAGE(elem_size) - \ 184 UNUSABLE_ELEMS_PER_PAGE(elem_size, mode))) 185 186 #define ECORE_CHAIN_PAGE_CNT(elem_cnt, elem_size, mode) \ 187 DIV_ROUND_UP(elem_cnt, USABLE_ELEMS_PER_PAGE(elem_size, mode)) 188 189 #define is_chain_u16(p) ((p)->cnt_type == ECORE_CHAIN_CNT_TYPE_U16) 190 #define is_chain_u32(p) ((p)->cnt_type == ECORE_CHAIN_CNT_TYPE_U32) 191 192 /* Accessors */ 193 static OSAL_INLINE u16 ecore_chain_get_prod_idx(struct ecore_chain *p_chain) 194 { 195 OSAL_ASSERT(is_chain_u16(p_chain)); 196 return p_chain->u.chain16.prod_idx; 197 } 198 199 #ifndef LINUX_REMOVE 200 static OSAL_INLINE u32 ecore_chain_get_prod_idx_u32(struct ecore_chain *p_chain) 201 { 202 OSAL_ASSERT(is_chain_u32(p_chain)); 203 return p_chain->u.chain32.prod_idx; 204 } 205 #endif 206 207 static OSAL_INLINE u16 ecore_chain_get_cons_idx(struct ecore_chain *p_chain) 208 { 209 OSAL_ASSERT(is_chain_u16(p_chain)); 210 return p_chain->u.chain16.cons_idx; 211 } 212 213 static OSAL_INLINE u32 ecore_chain_get_cons_idx_u32(struct ecore_chain *p_chain) 214 { 215 OSAL_ASSERT(is_chain_u32(p_chain)); 216 return p_chain->u.chain32.cons_idx; 217 } 218 219 /* FIXME: 220 * Should create OSALs for the below definitions. 221 * For Linux, replace them with the existing U16_MAX and U32_MAX, and handle 222 * kernel versions that lack them. 223 */ 224 #define ECORE_U16_MAX ((u16)~0U) 225 #define ECORE_U32_MAX ((u32)~0U) 226 227 static OSAL_INLINE u16 ecore_chain_get_elem_left(struct ecore_chain *p_chain) 228 { 229 u16 used; 230 231 OSAL_ASSERT(is_chain_u16(p_chain)); 232 233 used = (u16)(((u32)ECORE_U16_MAX + 1 + 234 (u32)(p_chain->u.chain16.prod_idx)) - 235 (u32)p_chain->u.chain16.cons_idx); 236 if (p_chain->mode == ECORE_CHAIN_MODE_NEXT_PTR) 237 used -= (((u32)ECORE_U16_MAX + 1) / p_chain->elem_per_page + 238 p_chain->u.chain16.prod_idx / p_chain->elem_per_page - 239 p_chain->u.chain16.cons_idx / p_chain->elem_per_page) % 240 p_chain->page_cnt; 241 242 return (u16)(p_chain->capacity - used); 243 } 244 245 static OSAL_INLINE u32 246 ecore_chain_get_elem_left_u32(struct ecore_chain *p_chain) 247 { 248 u32 used; 249 250 OSAL_ASSERT(is_chain_u32(p_chain)); 251 252 used = (u32)(((u64)ECORE_U32_MAX + 1 + 253 (u64)(p_chain->u.chain32.prod_idx)) - 254 (u64)p_chain->u.chain32.cons_idx); 255 if (p_chain->mode == ECORE_CHAIN_MODE_NEXT_PTR) 256 used -= (((u64)ECORE_U32_MAX + 1) / p_chain->elem_per_page + 257 p_chain->u.chain32.prod_idx / p_chain->elem_per_page - 258 p_chain->u.chain32.cons_idx / p_chain->elem_per_page) % 259 p_chain->page_cnt; 260 261 return p_chain->capacity - used; 262 } 263 264 #ifndef LINUX_REMOVE 265 static OSAL_INLINE u8 ecore_chain_is_full(struct ecore_chain *p_chain) 266 { 267 if (is_chain_u16(p_chain)) 268 return (ecore_chain_get_elem_left(p_chain) == 269 p_chain->capacity); 270 else 271 return (ecore_chain_get_elem_left_u32(p_chain) == 272 p_chain->capacity); 273 } 274 275 static OSAL_INLINE u8 ecore_chain_is_empty(struct ecore_chain *p_chain) 276 { 277 if (is_chain_u16(p_chain)) 278 return (ecore_chain_get_elem_left(p_chain) == 0); 279 else 280 return (ecore_chain_get_elem_left_u32(p_chain) == 0); 281 } 282 283 static OSAL_INLINE 284 u16 ecore_chain_get_elem_per_page(struct ecore_chain *p_chain) 285 { 286 return p_chain->elem_per_page; 287 } 288 #endif 289 290 static OSAL_INLINE 291 u16 ecore_chain_get_usable_per_page(struct ecore_chain *p_chain) 292 { 293 return p_chain->usable_per_page; 294 } 295 296 static OSAL_INLINE 297 u8 ecore_chain_get_unusable_per_page(struct ecore_chain *p_chain) 298 { 299 return p_chain->elem_unusable; 300 } 301 302 #ifndef LINUX_REMOVE 303 static OSAL_INLINE u32 ecore_chain_get_size(struct ecore_chain *p_chain) 304 { 305 return p_chain->size; 306 } 307 #endif 308 309 static OSAL_INLINE u32 ecore_chain_get_page_cnt(struct ecore_chain *p_chain) 310 { 311 return p_chain->page_cnt; 312 } 313 314 static OSAL_INLINE 315 dma_addr_t ecore_chain_get_pbl_phys(struct ecore_chain *p_chain) 316 { 317 return p_chain->pbl_sp.p_phys_table; 318 } 319 320 /** 321 * @brief ecore_chain_advance_page - 322 * 323 * Advance the next element accros pages for a linked chain 324 * 325 * @param p_chain 326 * @param p_next_elem 327 * @param idx_to_inc 328 * @param page_to_inc 329 */ 330 static OSAL_INLINE void 331 ecore_chain_advance_page(struct ecore_chain *p_chain, void **p_next_elem, 332 void *idx_to_inc, void *page_to_inc) 333 { 334 struct ecore_chain_next *p_next = OSAL_NULL; 335 u32 page_index = 0; 336 337 switch(p_chain->mode) { 338 case ECORE_CHAIN_MODE_NEXT_PTR: 339 p_next = (struct ecore_chain_next *)(*p_next_elem); 340 *p_next_elem = p_next->next_virt; 341 if (is_chain_u16(p_chain)) 342 *(u16 *)idx_to_inc += (u16)p_chain->elem_unusable; 343 else 344 *(u32 *)idx_to_inc += (u16)p_chain->elem_unusable; 345 break; 346 case ECORE_CHAIN_MODE_SINGLE: 347 *p_next_elem = p_chain->p_virt_addr; 348 break; 349 case ECORE_CHAIN_MODE_PBL: 350 if (is_chain_u16(p_chain)) { 351 if (++(*(u16 *)page_to_inc) == p_chain->page_cnt) 352 *(u16 *)page_to_inc = 0; 353 page_index = *(u16 *)page_to_inc; 354 } else { 355 if (++(*(u32 *)page_to_inc) == p_chain->page_cnt) 356 *(u32 *)page_to_inc = 0; 357 page_index = *(u32 *)page_to_inc; 358 } 359 *p_next_elem = p_chain->pbl.pp_virt_addr_tbl[page_index]; 360 } 361 } 362 363 #define is_unusable_idx(p, idx) \ 364 (((p)->u.chain16.idx & (p)->elem_per_page_mask) == (p)->usable_per_page) 365 366 #define is_unusable_idx_u32(p, idx) \ 367 (((p)->u.chain32.idx & (p)->elem_per_page_mask) == (p)->usable_per_page) 368 369 #define is_unusable_next_idx(p, idx) \ 370 ((((p)->u.chain16.idx + 1) & (p)->elem_per_page_mask) == (p)->usable_per_page) 371 372 #define is_unusable_next_idx_u32(p, idx) \ 373 ((((p)->u.chain32.idx + 1) & (p)->elem_per_page_mask) == (p)->usable_per_page) 374 375 #define test_and_skip(p, idx) \ 376 do { \ 377 if (is_chain_u16(p)) { \ 378 if (is_unusable_idx(p, idx)) \ 379 (p)->u.chain16.idx += (p)->elem_unusable; \ 380 } else { \ 381 if (is_unusable_idx_u32(p, idx)) \ 382 (p)->u.chain32.idx += (p)->elem_unusable; \ 383 } \ 384 } while (0) 385 386 #ifndef LINUX_REMOVE 387 /** 388 * @brief ecore_chain_return_multi_produced - 389 * 390 * A chain in which the driver "Produces" elements should use this API 391 * to indicate previous produced elements are now consumed. 392 * 393 * @param p_chain 394 * @param num 395 */ 396 static OSAL_INLINE 397 void ecore_chain_return_multi_produced(struct ecore_chain *p_chain, u32 num) 398 { 399 if (is_chain_u16(p_chain)) 400 p_chain->u.chain16.cons_idx += (u16)num; 401 else 402 p_chain->u.chain32.cons_idx += num; 403 test_and_skip(p_chain, cons_idx); 404 } 405 #endif 406 407 /** 408 * @brief ecore_chain_return_produced - 409 * 410 * A chain in which the driver "Produces" elements should use this API 411 * to indicate previous produced elements are now consumed. 412 * 413 * @param p_chain 414 */ 415 static OSAL_INLINE void ecore_chain_return_produced(struct ecore_chain *p_chain) 416 { 417 if (is_chain_u16(p_chain)) 418 p_chain->u.chain16.cons_idx++; 419 else 420 p_chain->u.chain32.cons_idx++; 421 test_and_skip(p_chain, cons_idx); 422 } 423 424 /** 425 * @brief ecore_chain_produce - 426 * 427 * A chain in which the driver "Produces" elements should use this to get 428 * a pointer to the next element which can be "Produced". It's driver 429 * responsibility to validate that the chain has room for new element. 430 * 431 * @param p_chain 432 * 433 * @return void*, a pointer to next element 434 */ 435 static OSAL_INLINE void *ecore_chain_produce(struct ecore_chain *p_chain) 436 { 437 void *p_ret = OSAL_NULL, *p_prod_idx, *p_prod_page_idx; 438 439 if (is_chain_u16(p_chain)) { 440 if ((p_chain->u.chain16.prod_idx & 441 p_chain->elem_per_page_mask) == 442 p_chain->next_page_mask) { 443 p_prod_idx = &p_chain->u.chain16.prod_idx; 444 p_prod_page_idx = &p_chain->pbl.c.pbl_u16.prod_page_idx; 445 ecore_chain_advance_page(p_chain, &p_chain->p_prod_elem, 446 p_prod_idx, p_prod_page_idx); 447 } 448 p_chain->u.chain16.prod_idx++; 449 } else { 450 if ((p_chain->u.chain32.prod_idx & 451 p_chain->elem_per_page_mask) == 452 p_chain->next_page_mask) { 453 p_prod_idx = &p_chain->u.chain32.prod_idx; 454 p_prod_page_idx = &p_chain->pbl.c.pbl_u32.prod_page_idx; 455 ecore_chain_advance_page(p_chain, &p_chain->p_prod_elem, 456 p_prod_idx, p_prod_page_idx); 457 } 458 p_chain->u.chain32.prod_idx++; 459 } 460 461 p_ret = p_chain->p_prod_elem; 462 p_chain->p_prod_elem = (void*)(((u8*)p_chain->p_prod_elem) + 463 p_chain->elem_size); 464 465 return p_ret; 466 } 467 468 /** 469 * @brief ecore_chain_get_capacity - 470 * 471 * Get the maximum number of BDs in chain 472 * 473 * @param p_chain 474 * @param num 475 * 476 * @return number of unusable BDs 477 */ 478 static OSAL_INLINE u32 ecore_chain_get_capacity(struct ecore_chain *p_chain) 479 { 480 return p_chain->capacity; 481 } 482 483 /** 484 * @brief ecore_chain_recycle_consumed - 485 * 486 * Returns an element which was previously consumed; 487 * Increments producers so they could be written to FW. 488 * 489 * @param p_chain 490 */ 491 static OSAL_INLINE 492 void ecore_chain_recycle_consumed(struct ecore_chain *p_chain) 493 { 494 test_and_skip(p_chain, prod_idx); 495 if (is_chain_u16(p_chain)) 496 p_chain->u.chain16.prod_idx++; 497 else 498 p_chain->u.chain32.prod_idx++; 499 } 500 501 /** 502 * @brief ecore_chain_consume - 503 * 504 * A Chain in which the driver utilizes data written by a different source 505 * (i.e., FW) should use this to access passed buffers. 506 * 507 * @param p_chain 508 * 509 * @return void*, a pointer to the next buffer written 510 */ 511 static OSAL_INLINE void *ecore_chain_consume(struct ecore_chain *p_chain) 512 { 513 void *p_ret = OSAL_NULL, *p_cons_idx, *p_cons_page_idx; 514 515 if (is_chain_u16(p_chain)) { 516 if ((p_chain->u.chain16.cons_idx & 517 p_chain->elem_per_page_mask) == 518 p_chain->next_page_mask) { 519 p_cons_idx = &p_chain->u.chain16.cons_idx; 520 p_cons_page_idx = &p_chain->pbl.c.pbl_u16.cons_page_idx; 521 ecore_chain_advance_page(p_chain, &p_chain->p_cons_elem, 522 p_cons_idx, p_cons_page_idx); 523 } 524 p_chain->u.chain16.cons_idx++; 525 } else { 526 if ((p_chain->u.chain32.cons_idx & 527 p_chain->elem_per_page_mask) == 528 p_chain->next_page_mask) { 529 p_cons_idx = &p_chain->u.chain32.cons_idx; 530 p_cons_page_idx = &p_chain->pbl.c.pbl_u32.cons_page_idx; 531 ecore_chain_advance_page(p_chain, &p_chain->p_cons_elem, 532 p_cons_idx, p_cons_page_idx); 533 } 534 p_chain->u.chain32.cons_idx++; 535 } 536 537 p_ret = p_chain->p_cons_elem; 538 p_chain->p_cons_elem = (void*)(((u8*)p_chain->p_cons_elem) + 539 p_chain->elem_size); 540 541 return p_ret; 542 } 543 544 /** 545 * @brief ecore_chain_reset - 546 * 547 * Resets the chain to its start state 548 * 549 * @param p_chain pointer to a previously allocted chain 550 */ 551 static OSAL_INLINE void ecore_chain_reset(struct ecore_chain *p_chain) 552 { 553 u32 i; 554 555 if (is_chain_u16(p_chain)) { 556 p_chain->u.chain16.prod_idx = 0; 557 p_chain->u.chain16.cons_idx = 0; 558 } else { 559 p_chain->u.chain32.prod_idx = 0; 560 p_chain->u.chain32.cons_idx = 0; 561 } 562 p_chain->p_cons_elem = p_chain->p_virt_addr; 563 p_chain->p_prod_elem = p_chain->p_virt_addr; 564 565 if (p_chain->mode == ECORE_CHAIN_MODE_PBL) { 566 /* Use "page_cnt-1" as a reset value for the prod/cons page's 567 * indices, to avoid unnecessary page advancing on the first 568 * call to ecore_chain_produce/consume. Instead, the indices 569 * will be advanced to page_cnt and then will be wrapped to 0. 570 */ 571 u32 reset_val = p_chain->page_cnt - 1; 572 573 if (is_chain_u16(p_chain)) { 574 p_chain->pbl.c.pbl_u16.prod_page_idx = (u16)reset_val; 575 p_chain->pbl.c.pbl_u16.cons_page_idx = (u16)reset_val; 576 } else { 577 p_chain->pbl.c.pbl_u32.prod_page_idx = reset_val; 578 p_chain->pbl.c.pbl_u32.cons_page_idx = reset_val; 579 } 580 } 581 582 switch (p_chain->intended_use) { 583 case ECORE_CHAIN_USE_TO_CONSUME: 584 /* produce empty elements */ 585 for (i = 0; i < p_chain->capacity; i++) 586 ecore_chain_recycle_consumed(p_chain); 587 break; 588 589 case ECORE_CHAIN_USE_TO_CONSUME_PRODUCE: 590 case ECORE_CHAIN_USE_TO_PRODUCE: 591 default: 592 /* Do nothing */ 593 break; 594 } 595 } 596 597 /** 598 * @brief ecore_chain_init_params - 599 * 600 * Initalizes a basic chain struct 601 * 602 * @param p_chain 603 * @param page_cnt number of pages in the allocated buffer 604 * @param elem_size size of each element in the chain 605 * @param intended_use 606 * @param mode 607 * @param cnt_type 608 * @param dp_ctx 609 */ 610 static OSAL_INLINE void 611 ecore_chain_init_params(struct ecore_chain *p_chain, u32 page_cnt, u8 elem_size, 612 enum ecore_chain_use_mode intended_use, 613 enum ecore_chain_mode mode, 614 enum ecore_chain_cnt_type cnt_type, void *dp_ctx) 615 { 616 /* chain fixed parameters */ 617 p_chain->p_virt_addr = OSAL_NULL; 618 p_chain->p_phys_addr = 0; 619 p_chain->elem_size = elem_size; 620 p_chain->intended_use = (u8)intended_use; 621 p_chain->mode = mode; 622 p_chain->cnt_type = (u8)cnt_type; 623 624 p_chain->elem_per_page = ELEMS_PER_PAGE(elem_size); 625 p_chain->usable_per_page = USABLE_ELEMS_PER_PAGE(elem_size, mode); 626 p_chain->elem_per_page_mask = p_chain->elem_per_page - 1; 627 p_chain->elem_unusable = UNUSABLE_ELEMS_PER_PAGE(elem_size, mode); 628 p_chain->next_page_mask = (p_chain->usable_per_page & 629 p_chain->elem_per_page_mask); 630 631 p_chain->page_cnt = page_cnt; 632 p_chain->capacity = p_chain->usable_per_page * page_cnt; 633 p_chain->size = p_chain->elem_per_page * page_cnt; 634 p_chain->b_external_pbl = false; 635 p_chain->pbl_sp.p_phys_table = 0; 636 p_chain->pbl_sp.p_virt_table = OSAL_NULL; 637 p_chain->pbl.pp_virt_addr_tbl = OSAL_NULL; 638 639 p_chain->dp_ctx = dp_ctx; 640 } 641 642 /** 643 * @brief ecore_chain_init_mem - 644 * 645 * Initalizes a basic chain struct with its chain buffers 646 * 647 * @param p_chain 648 * @param p_virt_addr virtual address of allocated buffer's beginning 649 * @param p_phys_addr physical address of allocated buffer's beginning 650 * 651 */ 652 static OSAL_INLINE void ecore_chain_init_mem(struct ecore_chain *p_chain, 653 void *p_virt_addr, 654 dma_addr_t p_phys_addr) 655 { 656 p_chain->p_virt_addr = p_virt_addr; 657 p_chain->p_phys_addr = p_phys_addr; 658 } 659 660 /** 661 * @brief ecore_chain_init_pbl_mem - 662 * 663 * Initalizes a basic chain struct with its pbl buffers 664 * 665 * @param p_chain 666 * @param p_virt_pbl pointer to a pre allocated side table which will hold 667 * virtual page addresses. 668 * @param p_phys_pbl pointer to a pre-allocated side table which will hold 669 * physical page addresses. 670 * @param pp_virt_addr_tbl 671 * pointer to a pre-allocated side table which will hold 672 * the virtual addresses of the chain pages. 673 * 674 */ 675 static OSAL_INLINE void ecore_chain_init_pbl_mem(struct ecore_chain *p_chain, 676 void *p_virt_pbl, 677 dma_addr_t p_phys_pbl, 678 void **pp_virt_addr_tbl) 679 { 680 p_chain->pbl_sp.p_phys_table = p_phys_pbl; 681 p_chain->pbl_sp.p_virt_table = p_virt_pbl; 682 p_chain->pbl.pp_virt_addr_tbl = pp_virt_addr_tbl; 683 } 684 685 /** 686 * @brief ecore_chain_init_next_ptr_elem - 687 * 688 * Initalizes a next pointer element 689 * 690 * @param p_chain 691 * @param p_virt_curr virtual address of a chain page of which the next 692 * pointer element is initialized 693 * @param p_virt_next virtual address of the next chain page 694 * @param p_phys_next physical address of the next chain page 695 * 696 */ 697 static OSAL_INLINE void 698 ecore_chain_init_next_ptr_elem(struct ecore_chain *p_chain, void *p_virt_curr, 699 void *p_virt_next, dma_addr_t p_phys_next) 700 { 701 struct ecore_chain_next *p_next; 702 u32 size; 703 704 size = p_chain->elem_size * p_chain->usable_per_page; 705 p_next = (struct ecore_chain_next *)((u8 *)p_virt_curr + size); 706 707 DMA_REGPAIR_LE(p_next->next_phys, p_phys_next); 708 709 p_next->next_virt = p_virt_next; 710 } 711 712 /** 713 * @brief ecore_chain_get_last_elem - 714 * 715 * Returns a pointer to the last element of the chain 716 * 717 * @param p_chain 718 * 719 * @return void* 720 */ 721 static OSAL_INLINE void *ecore_chain_get_last_elem(struct ecore_chain *p_chain) 722 { 723 struct ecore_chain_next *p_next = OSAL_NULL; 724 void *p_virt_addr = OSAL_NULL; 725 u32 size, last_page_idx; 726 727 if (!p_chain->p_virt_addr) 728 goto out; 729 730 switch (p_chain->mode) { 731 case ECORE_CHAIN_MODE_NEXT_PTR: 732 size = p_chain->elem_size * p_chain->usable_per_page; 733 p_virt_addr = p_chain->p_virt_addr; 734 p_next = (struct ecore_chain_next *)((u8 *)p_virt_addr + size); 735 while (p_next->next_virt != p_chain->p_virt_addr) { 736 p_virt_addr = p_next->next_virt; 737 p_next = (struct ecore_chain_next *)((u8 *)p_virt_addr + 738 size); 739 } 740 break; 741 case ECORE_CHAIN_MODE_SINGLE: 742 p_virt_addr = p_chain->p_virt_addr; 743 break; 744 case ECORE_CHAIN_MODE_PBL: 745 last_page_idx = p_chain->page_cnt - 1; 746 p_virt_addr = p_chain->pbl.pp_virt_addr_tbl[last_page_idx]; 747 break; 748 } 749 /* p_virt_addr points at this stage to the last page of the chain */ 750 size = p_chain->elem_size * (p_chain->usable_per_page - 1); 751 p_virt_addr = (u8 *)p_virt_addr + size; 752 out: 753 return p_virt_addr; 754 } 755 756 /** 757 * @brief ecore_chain_set_prod - sets the prod to the given value 758 * 759 * @param prod_idx 760 * @param p_prod_elem 761 */ 762 static OSAL_INLINE void ecore_chain_set_prod(struct ecore_chain *p_chain, 763 u32 prod_idx, void *p_prod_elem) 764 { 765 if (p_chain->mode == ECORE_CHAIN_MODE_PBL) { 766 /* Use "prod_idx-1" since ecore_chain_produce() advances the 767 * page index before the producer index when getting to 768 * "next_page_mask". 769 */ 770 u32 elem_idx = 771 (prod_idx - 1 + p_chain->capacity) % p_chain->capacity; 772 u32 page_idx = elem_idx / p_chain->elem_per_page; 773 774 if (is_chain_u16(p_chain)) 775 p_chain->pbl.c.pbl_u16.prod_page_idx = (u16)page_idx; 776 else 777 p_chain->pbl.c.pbl_u32.prod_page_idx = page_idx; 778 } 779 780 if (is_chain_u16(p_chain)) 781 p_chain->u.chain16.prod_idx = (u16)prod_idx; 782 else 783 p_chain->u.chain32.prod_idx = prod_idx; 784 p_chain->p_prod_elem = p_prod_elem; 785 } 786 787 /** 788 * @brief ecore_chain_set_cons - sets the cons to the given value 789 * 790 * @param cons_idx 791 * @param p_cons_elem 792 */ 793 static OSAL_INLINE void ecore_chain_set_cons(struct ecore_chain *p_chain, 794 u32 cons_idx, void *p_cons_elem) 795 { 796 if (p_chain->mode == ECORE_CHAIN_MODE_PBL) { 797 /* Use "cons_idx-1" since ecore_chain_consume() advances the 798 * page index before the consumer index when getting to 799 * "next_page_mask". 800 */ 801 u32 elem_idx = 802 (cons_idx - 1 + p_chain->capacity) % p_chain->capacity; 803 u32 page_idx = elem_idx / p_chain->elem_per_page; 804 805 if (is_chain_u16(p_chain)) 806 p_chain->pbl.c.pbl_u16.cons_page_idx = (u16)page_idx; 807 else 808 p_chain->pbl.c.pbl_u32.cons_page_idx = page_idx; 809 } 810 811 if (is_chain_u16(p_chain)) 812 p_chain->u.chain16.cons_idx = (u16)cons_idx; 813 else 814 p_chain->u.chain32.cons_idx = cons_idx; 815 816 p_chain->p_cons_elem = p_cons_elem; 817 } 818 819 /** 820 * @brief ecore_chain_pbl_zero_mem - set chain memory to 0 821 * 822 * @param p_chain 823 */ 824 static OSAL_INLINE void ecore_chain_pbl_zero_mem(struct ecore_chain *p_chain) 825 { 826 u32 i, page_cnt; 827 828 if (p_chain->mode != ECORE_CHAIN_MODE_PBL) 829 return; 830 831 page_cnt = ecore_chain_get_page_cnt(p_chain); 832 833 for (i = 0; i < page_cnt; i++) 834 OSAL_MEM_ZERO(p_chain->pbl.pp_virt_addr_tbl[i], 835 ECORE_CHAIN_PAGE_SIZE); 836 } 837 838 int ecore_chain_print(struct ecore_chain *p_chain, char *buffer, 839 u32 buffer_size, u32 *element_indx, u32 stop_indx, 840 bool print_metadata, 841 int (*func_ptr_print_element)(struct ecore_chain *p_chain, 842 void *p_element, 843 char *buffer), 844 int (*func_ptr_print_metadata)(struct ecore_chain *p_chain, 845 char *buffer)); 846 847 #endif /* __ECORE_CHAIN_H__ */ 848