root/drivers/scsi/csiostor/csio_wr.h
/*
 * This file is part of the Chelsio FCoE driver for Linux.
 *
 * Copyright (c) 2008-2012 Chelsio Communications, Inc. All rights reserved.
 *
 * This software is available to you under a choice of one of two
 * licenses.  You may choose to be licensed under the terms of the GNU
 * General Public License (GPL) Version 2, available from the file
 * COPYING in the main directory of this source tree, or the
 * OpenIB.org BSD license below:
 *
 *     Redistribution and use in source and binary forms, with or
 *     without modification, are permitted provided that the following
 *     conditions are met:
 *
 *      - Redistributions of source code must retain the above
 *        copyright notice, this list of conditions and the following
 *        disclaimer.
 *
 *      - Redistributions in binary form must reproduce the above
 *        copyright notice, this list of conditions and the following
 *        disclaimer in the documentation and/or other materials
 *        provided with the distribution.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#ifndef __CSIO_WR_H__
#define __CSIO_WR_H__

#include <linux/cache.h>

#include "csio_defs.h"
#include "t4fw_api.h"
#include "t4fw_api_stor.h"

/*
 * SGE register field values.
 */
#define X_INGPCIEBOUNDARY_32B           0
#define X_INGPCIEBOUNDARY_64B           1
#define X_INGPCIEBOUNDARY_128B          2
#define X_INGPCIEBOUNDARY_256B          3
#define X_INGPCIEBOUNDARY_512B          4
#define X_INGPCIEBOUNDARY_1024B         5
#define X_INGPCIEBOUNDARY_2048B         6
#define X_INGPCIEBOUNDARY_4096B         7

/* GTS register */
#define X_TIMERREG_COUNTER0             0
#define X_TIMERREG_COUNTER1             1
#define X_TIMERREG_COUNTER2             2
#define X_TIMERREG_COUNTER3             3
#define X_TIMERREG_COUNTER4             4
#define X_TIMERREG_COUNTER5             5
#define X_TIMERREG_RESTART_COUNTER      6
#define X_TIMERREG_UPDATE_CIDX          7

/*
 * Egress Context field values
 */
#define X_FETCHBURSTMIN_16B             0
#define X_FETCHBURSTMIN_32B             1
#define X_FETCHBURSTMIN_64B             2
#define X_FETCHBURSTMIN_128B            3

#define X_FETCHBURSTMAX_64B             0
#define X_FETCHBURSTMAX_128B            1
#define X_FETCHBURSTMAX_256B            2
#define X_FETCHBURSTMAX_512B            3

#define X_HOSTFCMODE_NONE               0
#define X_HOSTFCMODE_INGRESS_QUEUE      1
#define X_HOSTFCMODE_STATUS_PAGE        2
#define X_HOSTFCMODE_BOTH               3

/*
 * Ingress Context field values
 */
#define X_UPDATESCHEDULING_TIMER        0
#define X_UPDATESCHEDULING_COUNTER_OPTTIMER     1

#define X_UPDATEDELIVERY_NONE           0
#define X_UPDATEDELIVERY_INTERRUPT      1
#define X_UPDATEDELIVERY_STATUS_PAGE    2
#define X_UPDATEDELIVERY_BOTH           3

#define X_INTERRUPTDESTINATION_PCIE     0
#define X_INTERRUPTDESTINATION_IQ       1

#define X_RSPD_TYPE_FLBUF               0
#define X_RSPD_TYPE_CPL                 1
#define X_RSPD_TYPE_INTR                2

/* WR status is at the same position as retval in a CMD header */
#define csio_wr_status(_wr)             \
                (FW_CMD_RETVAL_G(ntohl(((struct fw_cmd_hdr *)(_wr))->lo)))

struct csio_hw;

extern int csio_intr_coalesce_cnt;
extern int csio_intr_coalesce_time;

/* Ingress queue params */
struct csio_iq_params {

        uint8_t         iq_start:1;
        uint8_t         iq_stop:1;
        uint8_t         pfn:3;

        uint8_t         vfn;

        uint16_t        physiqid;
        uint16_t        iqid;

        uint16_t        fl0id;
        uint16_t        fl1id;

        uint8_t         viid;

        uint8_t         type;
        uint8_t         iqasynch;
        uint8_t         reserved4;

        uint8_t         iqandst;
        uint8_t         iqanus;
        uint8_t         iqanud;

        uint16_t        iqandstindex;

        uint8_t         iqdroprss;
        uint8_t         iqpciech;
        uint8_t         iqdcaen;

        uint8_t         iqdcacpu;
        uint8_t         iqintcntthresh;
        uint8_t         iqo;

        uint8_t         iqcprio;
        uint8_t         iqesize;

        uint16_t        iqsize;

        uint64_t        iqaddr;

        uint8_t         iqflintiqhsen;
        uint8_t         reserved5;
        uint8_t         iqflintcongen;
        uint8_t         iqflintcngchmap;

        uint32_t        reserved6;

        uint8_t         fl0hostfcmode;
        uint8_t         fl0cprio;
        uint8_t         fl0paden;
        uint8_t         fl0packen;
        uint8_t         fl0congen;
        uint8_t         fl0dcaen;

        uint8_t         fl0dcacpu;
        uint8_t         fl0fbmin;

        uint8_t         fl0fbmax;
        uint8_t         fl0cidxfthresho;
        uint8_t         fl0cidxfthresh;

        uint16_t        fl0size;

        uint64_t        fl0addr;

        uint64_t        reserved7;

        uint8_t         fl1hostfcmode;
        uint8_t         fl1cprio;
        uint8_t         fl1paden;
        uint8_t         fl1packen;
        uint8_t         fl1congen;
        uint8_t         fl1dcaen;

        uint8_t         fl1dcacpu;
        uint8_t         fl1fbmin;

        uint8_t         fl1fbmax;
        uint8_t         fl1cidxfthresho;
        uint8_t         fl1cidxfthresh;

        uint16_t        fl1size;

        uint64_t        fl1addr;
};

/* Egress queue params */
struct csio_eq_params {

        uint8_t         pfn;
        uint8_t         vfn;

        uint8_t         eqstart:1;
        uint8_t         eqstop:1;

        uint16_t        physeqid;
        uint32_t        eqid;

        uint8_t         hostfcmode:2;
        uint8_t         cprio:1;
        uint8_t         pciechn:3;

        uint16_t        iqid;

        uint8_t         dcaen:1;
        uint8_t         dcacpu:5;

        uint8_t         fbmin:3;
        uint8_t         fbmax:3;

        uint8_t         cidxfthresho:1;
        uint8_t         cidxfthresh:3;

        uint16_t        eqsize;

        uint64_t        eqaddr;
};

struct csio_dma_buf {
        struct list_head        list;
        void                    *vaddr;         /* Virtual address */
        dma_addr_t              paddr;          /* Physical address */
        uint32_t                len;            /* Buffer size */
};

/* Generic I/O request structure */
struct csio_ioreq {
        struct csio_sm          sm;             /* SM, List
                                                 * should be the first member
                                                 */
        int                     iq_idx;         /* Ingress queue index */
        int                     eq_idx;         /* Egress queue index */
        uint32_t                nsge;           /* Number of SG elements */
        uint32_t                tmo;            /* Driver timeout */
        uint32_t                datadir;        /* Data direction */
        struct csio_dma_buf     dma_buf;        /* Req/resp DMA buffers */
        uint16_t                wr_status;      /* WR completion status */
        int16_t                 drv_status;     /* Driver internal status */
        struct csio_lnode       *lnode;         /* Owner lnode */
        struct csio_rnode       *rnode;         /* Src/destination rnode */
        void (*io_cbfn) (struct csio_hw *, struct csio_ioreq *);
                                                /* completion callback */
        void                    *scratch1;      /* Scratch area 1.
                                                 */
        void                    *scratch2;      /* Scratch area 2. */
        struct list_head        gen_list;       /* Any list associated with
                                                 * this ioreq.
                                                 */
        uint64_t                fw_handle;      /* Unique handle passed
                                                 * to FW
                                                 */
        uint8_t                 dcopy;          /* Data copy required */
        uint8_t                 reserved1;
        uint16_t                reserved2;
        struct completion       cmplobj;        /* ioreq completion object */
} ____cacheline_aligned_in_smp;

/*
 * Egress status page for egress cidx updates
 */
struct csio_qstatus_page {
        __be32 qid;
        __be16 cidx;
        __be16 pidx;
};


enum {
        CSIO_MAX_FLBUF_PER_IQWR = 4,
        CSIO_QCREDIT_SZ  = 64,                  /* pidx/cidx increments
                                                 * in bytes
                                                 */
        CSIO_MAX_QID = 0xFFFF,
        CSIO_MAX_IQ = 128,

        CSIO_SGE_NTIMERS = 6,
        CSIO_SGE_NCOUNTERS = 4,
        CSIO_SGE_FL_SIZE_REGS = 16,
};

/* Defines for type */
enum {
        CSIO_EGRESS     = 1,
        CSIO_INGRESS    = 2,
        CSIO_FREELIST   = 3,
};

/*
 * Structure for footer (last 2 flits) of Ingress Queue Entry.
 */
struct csio_iqwr_footer {
        __be32                  hdrbuflen_pidx;
        __be32                  pldbuflen_qid;
        union {
                u8              type_gen;
                __be64          last_flit;
        } u;
};

#define IQWRF_NEWBUF            (1 << 31)
#define IQWRF_LEN_GET(x)        (((x) >> 0) & 0x7fffffffU)
#define IQWRF_GEN_SHIFT         7
#define IQWRF_TYPE_GET(x)       (((x) >> 4) & 0x3U)


/*
 * WR pair:
 * ========
 * A WR can start towards the end of a queue, and then continue at the
 * beginning, since the queue is considered to be circular. This will
 * require a pair of address/len to be passed back to the caller -
 * hence the Work request pair structure.
 */
struct csio_wr_pair {
        void                    *addr1;
        uint32_t                size1;
        void                    *addr2;
        uint32_t                size2;
};

/*
 * The following structure is used by ingress processing to return the
 * free list buffers to consumers.
 */
struct csio_fl_dma_buf {
        struct csio_dma_buf     flbufs[CSIO_MAX_FLBUF_PER_IQWR];
                                                /* Freelist DMA buffers */
        int                     offset;         /* Offset within the
                                                 * first FL buf.
                                                 */
        uint32_t                totlen;         /* Total length */
        uint8_t                 defer_free;     /* Free of buffer can
                                                 * deferred
                                                 */
};

/* Data-types */
typedef void (*iq_handler_t)(struct csio_hw *, void *, uint32_t,
                             struct csio_fl_dma_buf *, void *);

struct csio_iq {
        uint16_t                iqid;           /* Queue ID */
        uint16_t                physiqid;       /* Physical Queue ID */
        uint16_t                genbit;         /* Generation bit,
                                                 * initially set to 1
                                                 */
        int                     flq_idx;        /* Freelist queue index */
        iq_handler_t            iq_intx_handler; /* IQ INTx handler routine */
};

struct csio_eq {
        uint16_t                eqid;           /* Qid */
        uint16_t                physeqid;       /* Physical Queue ID */
        uint8_t                 wrap[512];      /* Temp area for q-wrap around*/
};

struct csio_fl {
        uint16_t                flid;           /* Qid */
        uint16_t                packen;         /* Packing enabled? */
        int                     offset;         /* Offset within FL buf */
        int                     sreg;           /* Size register */
        struct csio_dma_buf     *bufs;          /* Free list buffer ptr array
                                                 * indexed using flq->cidx/pidx
                                                 */
};

struct csio_qstats {
        uint32_t        n_tot_reqs;             /* Total no. of Requests */
        uint32_t        n_tot_rsps;             /* Total no. of responses */
        uint32_t        n_qwrap;                /* Queue wraps */
        uint32_t        n_eq_wr_split;          /* Number of split EQ WRs */
        uint32_t        n_qentry;               /* Queue entry */
        uint32_t        n_qempty;               /* Queue empty */
        uint32_t        n_qfull;                /* Queue fulls */
        uint32_t        n_rsp_unknown;          /* Unknown response type */
        uint32_t        n_stray_comp;           /* Stray completion intr */
        uint32_t        n_flq_refill;           /* Number of FL refills */
};

/* Queue metadata */
struct csio_q {
        uint16_t                type;           /* Type: Ingress/Egress/FL */
        uint16_t                pidx;           /* producer index */
        uint16_t                cidx;           /* consumer index */
        uint16_t                inc_idx;        /* Incremental index */
        uint32_t                wr_sz;          /* Size of all WRs in this q
                                                 * if fixed
                                                 */
        void                    *vstart;        /* Base virtual address
                                                 * of queue
                                                 */
        void                    *vwrap;         /* Virtual end address to
                                                 * wrap around at
                                                 */
        uint32_t                credits;        /* Size of queue in credits */
        void                    *owner;         /* Owner */
        union {                                 /* Queue contexts */
                struct csio_iq  iq;
                struct csio_eq  eq;
                struct csio_fl  fl;
        } un;

        dma_addr_t              pstart;         /* Base physical address of
                                                 * queue
                                                 */
        uint32_t                portid;         /* PCIE Channel */
        uint32_t                size;           /* Size of queue in bytes */
        struct csio_qstats      stats;          /* Statistics */
} ____cacheline_aligned_in_smp;

struct csio_sge {
        uint32_t        csio_fl_align;          /* Calculated and cached
                                                 * for fast path
                                                 */
        uint32_t        sge_control;            /* padding, boundaries,
                                                 * lengths, etc.
                                                 */
        uint32_t        sge_host_page_size;     /* Host page size */
        uint32_t        sge_fl_buf_size[CSIO_SGE_FL_SIZE_REGS];
                                                /* free list buffer sizes */
        uint16_t        timer_val[CSIO_SGE_NTIMERS];
        uint8_t         counter_val[CSIO_SGE_NCOUNTERS];
};

/* Work request module */
struct csio_wrm {
        int                     num_q;          /* Number of queues */
        struct csio_q           **q_arr;        /* Array of queue pointers
                                                 * allocated dynamically
                                                 * based on configured values
                                                 */
        uint32_t                fw_iq_start;    /* Start ID of IQ for this fn*/
        uint32_t                fw_eq_start;    /* Start ID of EQ for this fn*/
        struct csio_q           *intr_map[CSIO_MAX_IQ];
                                                /* IQ-id to IQ map table. */
        int                     free_qidx;      /* queue idx of free queue */
        struct csio_sge         sge;            /* SGE params */
};

#define csio_get_q(__hw, __idx)         ((__hw)->wrm.q_arr[__idx])
#define csio_q_type(__hw, __idx)        ((__hw)->wrm.q_arr[(__idx)]->type)
#define csio_q_pidx(__hw, __idx)        ((__hw)->wrm.q_arr[(__idx)]->pidx)
#define csio_q_cidx(__hw, __idx)        ((__hw)->wrm.q_arr[(__idx)]->cidx)
#define csio_q_inc_idx(__hw, __idx)     ((__hw)->wrm.q_arr[(__idx)]->inc_idx)
#define csio_q_vstart(__hw, __idx)      ((__hw)->wrm.q_arr[(__idx)]->vstart)
#define csio_q_pstart(__hw, __idx)      ((__hw)->wrm.q_arr[(__idx)]->pstart)
#define csio_q_size(__hw, __idx)        ((__hw)->wrm.q_arr[(__idx)]->size)
#define csio_q_credits(__hw, __idx)     ((__hw)->wrm.q_arr[(__idx)]->credits)
#define csio_q_portid(__hw, __idx)      ((__hw)->wrm.q_arr[(__idx)]->portid)
#define csio_q_wr_sz(__hw, __idx)       ((__hw)->wrm.q_arr[(__idx)]->wr_sz)
#define csio_q_iqid(__hw, __idx)        ((__hw)->wrm.q_arr[(__idx)]->un.iq.iqid)
#define csio_q_physiqid(__hw, __idx)                                    \
                                ((__hw)->wrm.q_arr[(__idx)]->un.iq.physiqid)
#define csio_q_iq_flq_idx(__hw, __idx)                                  \
                                ((__hw)->wrm.q_arr[(__idx)]->un.iq.flq_idx)
#define csio_q_eqid(__hw, __idx)        ((__hw)->wrm.q_arr[(__idx)]->un.eq.eqid)
#define csio_q_flid(__hw, __idx)        ((__hw)->wrm.q_arr[(__idx)]->un.fl.flid)

#define csio_q_physeqid(__hw, __idx)                                    \
                                ((__hw)->wrm.q_arr[(__idx)]->un.eq.physeqid)
#define csio_iq_has_fl(__iq)            ((__iq)->un.iq.flq_idx != -1)

#define csio_q_iq_to_flid(__hw, __iq_idx)                               \
        csio_q_flid((__hw), (__hw)->wrm.q_arr[(__iq_qidx)]->un.iq.flq_idx)
#define csio_q_set_intr_map(__hw, __iq_idx, __rel_iq_id)                \
                (__hw)->wrm.intr_map[__rel_iq_id] = csio_get_q(__hw, __iq_idx)
#define csio_q_eq_wrap(__hw, __idx)     ((__hw)->wrm.q_arr[(__idx)]->un.eq.wrap)

struct csio_mb;

int csio_wr_alloc_q(struct csio_hw *, uint32_t, uint32_t,
                    uint16_t, void *, uint32_t, int, iq_handler_t);
int csio_wr_iq_create(struct csio_hw *, void *, int,
                                uint32_t, uint8_t, bool,
                                void (*)(struct csio_hw *, struct csio_mb *));
int csio_wr_eq_create(struct csio_hw *, void *, int, int, uint8_t,
                                void (*)(struct csio_hw *, struct csio_mb *));
int csio_wr_destroy_queues(struct csio_hw *, bool cmd);


int csio_wr_get(struct csio_hw *, int, uint32_t,
                          struct csio_wr_pair *);
void csio_wr_copy_to_wrp(void *, struct csio_wr_pair *, uint32_t, uint32_t);
int csio_wr_issue(struct csio_hw *, int, bool);
int csio_wr_process_iq(struct csio_hw *, struct csio_q *,
                                 void (*)(struct csio_hw *, void *,
                                          uint32_t, struct csio_fl_dma_buf *,
                                          void *),
                                 void *);
int csio_wr_process_iq_idx(struct csio_hw *, int,
                                 void (*)(struct csio_hw *, void *,
                                          uint32_t, struct csio_fl_dma_buf *,
                                          void *),
                                 void *);

void csio_wr_sge_init(struct csio_hw *);
int csio_wrm_init(struct csio_wrm *, struct csio_hw *);
void csio_wrm_exit(struct csio_wrm *, struct csio_hw *);

#endif /* ifndef __CSIO_WR_H__ */