linux/include/linux/qed/qed_chain.h
<<
>>
Prefs
   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))
  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)
  26
  27#define HILO_GEN(hi, lo, type)  ((((type)(hi)) << 32) + (lo))
  28#define HILO_64(hi, lo) HILO_GEN((le32_to_cpu(hi)), (le32_to_cpu(lo)), u64)
  29#define HILO_64_REGPAIR(regpair)        (HILO_64(regpair.hi, regpair.lo))
  30#define HILO_DMA_REGPAIR(regpair)       ((dma_addr_t)HILO_64_REGPAIR(regpair))
  31
  32enum 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
  43enum 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
  49enum 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
  57struct qed_chain_next {
  58        struct regpair  next_phys;
  59        void            *next_virt;
  60};
  61
  62struct qed_chain_pbl_u16 {
  63        u16 prod_page_idx;
  64        u16 cons_page_idx;
  65};
  66
  67struct qed_chain_pbl_u32 {
  68        u32 prod_page_idx;
  69        u32 cons_page_idx;
  70};
  71
  72struct qed_chain_pbl {
  73        /* Base address of a pre-allocated buffer for pbl */
  74        dma_addr_t      p_phys_table;
  75        void            *p_virt_table;
  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
  89struct qed_chain_u16 {
  90        /* Cyclic index of next element to produce/consme */
  91        u16 prod_idx;
  92        u16 cons_idx;
  93};
  94
  95struct qed_chain_u32 {
  96        /* Cyclic index of next element to produce/consme */
  97        u32 prod_idx;
  98        u32 cons_idx;
  99};
 100
 101struct qed_chain {
 102        void                    *p_virt_addr;
 103        dma_addr_t              p_phys_addr;
 104        void                    *p_prod_elem;
 105        void                    *p_cons_elem;
 106
 107        enum qed_chain_mode     mode;
 108        enum qed_chain_use_mode intended_use; /* used to produce/consume */
 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 */
 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
 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
 153/* Accessors */
 154static inline u16 qed_chain_get_prod_idx(struct qed_chain *p_chain)
 155{
 156        return p_chain->u.chain16.prod_idx;
 157}
 158
 159static inline u16 qed_chain_get_cons_idx(struct qed_chain *p_chain)
 160{
 161        return p_chain->u.chain16.cons_idx;
 162}
 163
 164static inline u32 qed_chain_get_cons_idx_u32(struct qed_chain *p_chain)
 165{
 166        return p_chain->u.chain32.cons_idx;
 167}
 168
 169static inline u16 qed_chain_get_elem_left(struct qed_chain *p_chain)
 170{
 171        u16 used;
 172
 173        used = (u16) (((u32)0x10000 +
 174                       (u32)p_chain->u.chain16.prod_idx) -
 175                      (u32)p_chain->u.chain16.cons_idx);
 176        if (p_chain->mode == QED_CHAIN_MODE_NEXT_PTR)
 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;
 179
 180        return (u16)(p_chain->capacity - used);
 181}
 182
 183static inline u32 qed_chain_get_elem_left_u32(struct qed_chain *p_chain)
 184{
 185        u32 used;
 186
 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;
 193
 194        return p_chain->capacity - used;
 195}
 196
 197static inline u16 qed_chain_get_usable_per_page(struct qed_chain *p_chain)
 198{
 199        return p_chain->usable_per_page;
 200}
 201
 202static inline u16 qed_chain_get_unusable_per_page(struct qed_chain *p_chain)
 203{
 204        return p_chain->elem_unusable;
 205}
 206
 207static inline u32 qed_chain_get_page_cnt(struct qed_chain *p_chain)
 208{
 209        return p_chain->page_cnt;
 210}
 211
 212static inline dma_addr_t qed_chain_get_pbl_phys(struct qed_chain *p_chain)
 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 */
 227static inline void
 228qed_chain_advance_page(struct qed_chain *p_chain,
 229                       void **p_next_elem, void *idx_to_inc, void *page_to_inc)
 230
 231{
 232        struct qed_chain_next *p_next = NULL;
 233        u32 page_index = 0;
 234        switch (p_chain->mode) {
 235        case QED_CHAIN_MODE_NEXT_PTR:
 236                p_next = *p_next_elem;
 237                *p_next_elem = p_next->next_virt;
 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;
 242                break;
 243        case QED_CHAIN_MODE_SINGLE:
 244                *p_next_elem = p_chain->p_virt_addr;
 245                break;
 246
 247        case QED_CHAIN_MODE_PBL:
 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;
 256                }
 257                *p_next_elem = p_chain->pbl.pp_virt_addr_tbl[page_index];
 258        }
 259}
 260
 261#define is_unusable_idx(p, idx) \
 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)
 269
 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)
 273
 274#define test_and_skip(p, idx)                                              \
 275        do {                                            \
 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;  \
 282                }                                       \
 283        } while (0)
 284
 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 */
 293static inline void qed_chain_return_produced(struct qed_chain *p_chain)
 294{
 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);
 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 */
 313static inline void *qed_chain_produce(struct qed_chain *p_chain)
 314{
 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++;
 335        }
 336
 337        p_ret = p_chain->p_prod_elem;
 338        p_chain->p_prod_elem = (void *)(((u8 *)p_chain->p_prod_elem) +
 339                                        p_chain->elem_size);
 340
 341        return p_ret;
 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 *
 352 * @return number of unusable BDs
 353 */
 354static inline u32 qed_chain_get_capacity(struct qed_chain *p_chain)
 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 */
 367static inline void qed_chain_recycle_consumed(struct qed_chain *p_chain)
 368{
 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++;
 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 */
 386static inline void *qed_chain_consume(struct qed_chain *p_chain)
 387{
 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;
 404                qed_chain_advance_page(p_chain, &p_chain->p_cons_elem,
 405                                               p_cons_idx, p_cons_page_idx);
 406                }
 407                p_chain->u.chain32.cons_idx++;
 408        }
 409
 410        p_ret = p_chain->p_cons_elem;
 411        p_chain->p_cons_elem = (void *)(((u8 *)p_chain->p_cons_elem) +
 412                                        p_chain->elem_size);
 413
 414        return p_ret;
 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 */
 422static inline void qed_chain_reset(struct qed_chain *p_chain)
 423{
 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;
 435
 436        if (p_chain->mode == QED_CHAIN_MODE_PBL) {
 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                }
 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 */
 478static 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)
 484{
 485        /* chain fixed parameters */
 486        p_chain->p_virt_addr = NULL;
 487        p_chain->p_phys_addr = 0;
 488        p_chain->elem_size      = elem_size;
 489        p_chain->intended_use = intended_use;
 490        p_chain->mode           = mode;
 491        p_chain->cnt_type = cnt_type;
 492
 493        p_chain->elem_per_page          = ELEMS_PER_PAGE(elem_size);
 494        p_chain->usable_per_page = USABLE_ELEMS_PER_PAGE(elem_size, mode);
 495        p_chain->elem_per_page_mask     = p_chain->elem_per_page - 1;
 496        p_chain->elem_unusable = UNUSABLE_ELEMS_PER_PAGE(elem_size, mode);
 497        p_chain->next_page_mask = (p_chain->usable_per_page &
 498                                   p_chain->elem_per_page_mask);
 499
 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;
 503
 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;
 507}
 508
 509/**
 510 * @brief qed_chain_init_mem -
 511 *
 512 * Initalizes a basic chain struct with its chain buffers
 513 *
 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
 517 *
 518 */
 519static inline void qed_chain_init_mem(struct qed_chain *p_chain,
 520                                      void *p_virt_addr, dma_addr_t p_phys_addr)
 521{
 522        p_chain->p_virt_addr = p_virt_addr;
 523        p_chain->p_phys_addr = p_phys_addr;
 524}
 525
 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 */
 541static 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{
 546        p_chain->pbl.p_phys_table = p_phys_pbl;
 547        p_chain->pbl.p_virt_table = p_virt_pbl;
 548        p_chain->pbl.pp_virt_addr_tbl = pp_virt_addr_tbl;
 549}
 550
 551/**
 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
 561 *
 562 */
 563static inline void
 564qed_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)
 567{
 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;
 577}
 578
 579/**
 580 * @brief qed_chain_get_last_elem -
 581 *
 582 * Returns a pointer to the last element of the chain
 583 *
 584 * @param p_chain
 585 *
 586 * @return void*
 587 */
 588static inline void *qed_chain_get_last_elem(struct qed_chain *p_chain)
 589{
 590        struct qed_chain_next *p_next = NULL;
 591        void *p_virt_addr = NULL;
 592        u32 size, last_page_idx;
 593
 594        if (!p_chain->p_virt_addr)
 595                goto out;
 596
 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;
 619out:
 620        return p_virt_addr;
 621}
 622
 623/**
 624 * @brief qed_chain_set_prod - sets the prod to the given value
 625 *
 626 * @param prod_idx
 627 * @param p_prod_elem
 628 */
 629static 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
 641 *
 642 * @param p_chain
 643 */
 644static inline void qed_chain_pbl_zero_mem(struct qed_chain *p_chain)
 645{
 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);
 656}
 657
 658#endif
 659