/* * 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. */ #include <linux/kernel.h> #include <linux/string.h> #include <linux/compiler.h> #include <linux/slab.h> #include <asm/page.h> #include <linux/cache.h> #include "t4_values.h" #include "csio_hw.h" #include "csio_wr.h" #include "csio_mb.h" #include "csio_defs.h" int csio_intr_coalesce_cnt; /* value:SGE_INGRESS_RX_THRESHOLD[0] */ static int csio_sge_thresh_reg; /* SGE_INGRESS_RX_THRESHOLD[0] */ int csio_intr_coalesce_time = …; /* value:SGE_TIMER_VALUE_1 */ static int csio_sge_timer_reg = …; #define CSIO_SET_FLBUF_SIZE(_hw, _reg, _val) … static void csio_get_flbuf_size(struct csio_hw *hw, struct csio_sge *sge, uint32_t reg) { … } /* Free list buffer size */ static inline uint32_t csio_wr_fl_bufsz(struct csio_sge *sge, struct csio_dma_buf *buf) { … } /* Size of the egress queue status page */ static inline uint32_t csio_wr_qstat_pgsz(struct csio_hw *hw) { … } /* Ring freelist doorbell */ static inline void csio_wr_ring_fldb(struct csio_hw *hw, struct csio_q *flq) { … } /* Write a 0 cidx increment value to enable SGE interrupts for this queue */ static void csio_wr_sge_intr_enable(struct csio_hw *hw, uint16_t iqid) { … } /* * csio_wr_fill_fl - Populate the FL buffers of a FL queue. * @hw: HW module. * @flq: Freelist queue. * * Fill up freelist buffer entries with buffers of size specified * in the size register. * */ static int csio_wr_fill_fl(struct csio_hw *hw, struct csio_q *flq) { … } /* * csio_wr_update_fl - * @hw: HW module. * @flq: Freelist queue. * * */ static inline void csio_wr_update_fl(struct csio_hw *hw, struct csio_q *flq, uint16_t n) { … } /* * csio_wr_alloc_q - Allocate a WR queue and initialize it. * @hw: HW module * @qsize: Size of the queue in bytes * @wrsize: Since of WR in this queue, if fixed. * @type: Type of queue (Ingress/Egress/Freelist) * @owner: Module that owns this queue. * @nflb: Number of freelist buffers for FL. * @sreg: What is the FL buffer size register? * @iq_int_handler: Ingress queue handler in INTx mode. * * This function allocates and sets up a queue for the caller * of size qsize, aligned at the required boundary. This is subject to * be free entries being available in the queue array. If one is found, * it is initialized with the allocated queue, marked as being used (owner), * and a handle returned to the caller in form of the queue's index * into the q_arr array. * If user has indicated a freelist (by specifying nflb > 0), create * another queue (with its own index into q_arr) for the freelist. Allocate * memory for DMA buffer metadata (vaddr, len etc). Save off the freelist * idx in the ingress queue's flq.idx. This is how a Freelist is associated * with its owning ingress queue. */ int csio_wr_alloc_q(struct csio_hw *hw, uint32_t qsize, uint32_t wrsize, uint16_t type, void *owner, uint32_t nflb, int sreg, iq_handler_t iq_intx_handler) { … } /* * csio_wr_iq_create_rsp - Response handler for IQ creation. * @hw: The HW module. * @mbp: Mailbox. * @iq_idx: Ingress queue that got created. * * Handle FW_IQ_CMD mailbox completion. Save off the assigned IQ/FL ids. */ static int csio_wr_iq_create_rsp(struct csio_hw *hw, struct csio_mb *mbp, int iq_idx) { … } /* * csio_wr_iq_create - Configure an Ingress queue with FW. * @hw: The HW module. * @priv: Private data object. * @iq_idx: Ingress queue index in the WR module. * @vec: MSIX vector. * @portid: PCIE Channel to be associated with this queue. * @async: Is this a FW asynchronous message handling queue? * @cbfn: Completion callback. * * This API configures an ingress queue with FW by issuing a FW_IQ_CMD mailbox * with alloc/write bits set. */ int csio_wr_iq_create(struct csio_hw *hw, void *priv, int iq_idx, uint32_t vec, uint8_t portid, bool async, void (*cbfn) (struct csio_hw *, struct csio_mb *)) { … } /* * csio_wr_eq_create_rsp - Response handler for EQ creation. * @hw: The HW module. * @mbp: Mailbox. * @eq_idx: Egress queue that got created. * * Handle FW_EQ_OFLD_CMD mailbox completion. Save off the assigned EQ ids. */ static int csio_wr_eq_cfg_rsp(struct csio_hw *hw, struct csio_mb *mbp, int eq_idx) { … } /* * csio_wr_eq_create - Configure an Egress queue with FW. * @hw: HW module. * @priv: Private data. * @eq_idx: Egress queue index in the WR module. * @iq_idx: Associated ingress queue index. * @cbfn: Completion callback. * * This API configures a offload egress queue with FW by issuing a * FW_EQ_OFLD_CMD (with alloc + write ) mailbox. */ int csio_wr_eq_create(struct csio_hw *hw, void *priv, int eq_idx, int iq_idx, uint8_t portid, void (*cbfn) (struct csio_hw *, struct csio_mb *)) { … } /* * csio_wr_iq_destroy_rsp - Response handler for IQ removal. * @hw: The HW module. * @mbp: Mailbox. * @iq_idx: Ingress queue that was freed. * * Handle FW_IQ_CMD (free) mailbox completion. */ static int csio_wr_iq_destroy_rsp(struct csio_hw *hw, struct csio_mb *mbp, int iq_idx) { … } /* * csio_wr_iq_destroy - Free an ingress queue. * @hw: The HW module. * @priv: Private data object. * @iq_idx: Ingress queue index to destroy * @cbfn: Completion callback. * * This API frees an ingress queue by issuing the FW_IQ_CMD * with the free bit set. */ static int csio_wr_iq_destroy(struct csio_hw *hw, void *priv, int iq_idx, void (*cbfn)(struct csio_hw *, struct csio_mb *)) { … } /* * csio_wr_eq_destroy_rsp - Response handler for OFLD EQ creation. * @hw: The HW module. * @mbp: Mailbox. * @eq_idx: Egress queue that was freed. * * Handle FW_OFLD_EQ_CMD (free) mailbox completion. */ static int csio_wr_eq_destroy_rsp(struct csio_hw *hw, struct csio_mb *mbp, int eq_idx) { … } /* * csio_wr_eq_destroy - Free an Egress queue. * @hw: The HW module. * @priv: Private data object. * @eq_idx: Egress queue index to destroy * @cbfn: Completion callback. * * This API frees an Egress queue by issuing the FW_EQ_OFLD_CMD * with the free bit set. */ static int csio_wr_eq_destroy(struct csio_hw *hw, void *priv, int eq_idx, void (*cbfn) (struct csio_hw *, struct csio_mb *)) { … } /* * csio_wr_cleanup_eq_stpg - Cleanup Egress queue status page * @hw: HW module * @qidx: Egress queue index * * Cleanup the Egress queue status page. */ static void csio_wr_cleanup_eq_stpg(struct csio_hw *hw, int qidx) { … } /* * csio_wr_cleanup_iq_ftr - Cleanup Footer entries in IQ * @hw: HW module * @qidx: Ingress queue index * * Cleanup the footer entries in the given ingress queue, * set to 1 the internal copy of genbit. */ static void csio_wr_cleanup_iq_ftr(struct csio_hw *hw, int qidx) { … } int csio_wr_destroy_queues(struct csio_hw *hw, bool cmd) { … } /* * csio_wr_get - Get requested size of WR entry/entries from queue. * @hw: HW module. * @qidx: Index of queue. * @size: Cumulative size of Work request(s). * @wrp: Work request pair. * * If requested credits are available, return the start address of the * work request in the work request pair. Set pidx accordingly and * return. * * NOTE about 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/size to be passed back to the caller - * hence Work request pair format. */ int csio_wr_get(struct csio_hw *hw, int qidx, uint32_t size, struct csio_wr_pair *wrp) { … } /* * csio_wr_copy_to_wrp - Copies given data into WR. * @data_buf - Data buffer * @wrp - Work request pair. * @wr_off - Work request offset. * @data_len - Data length. * * Copies the given data in Work Request. Work request pair(wrp) specifies * address information of Work request. * Returns: none */ void csio_wr_copy_to_wrp(void *data_buf, struct csio_wr_pair *wrp, uint32_t wr_off, uint32_t data_len) { … } /* * csio_wr_issue - Notify chip of Work request. * @hw: HW module. * @qidx: Index of queue. * @prio: 0: Low priority, 1: High priority * * Rings the SGE Doorbell by writing the current producer index of the passed * in queue into the register. * */ int csio_wr_issue(struct csio_hw *hw, int qidx, bool prio) { … } static inline uint32_t csio_wr_avail_qcredits(struct csio_q *q) { … } /* * csio_wr_inval_flq_buf - Invalidate a free list buffer entry. * @hw: HW module. * @flq: The freelist queue. * * Invalidate the driver's version of a freelist buffer entry, * without freeing the associated the DMA memory. The entry * to be invalidated is picked up from the current Free list * queue cidx. * */ static inline void csio_wr_inval_flq_buf(struct csio_hw *hw, struct csio_q *flq) { … } /* * csio_wr_process_fl - Process a freelist completion. * @hw: HW module. * @q: The ingress queue attached to the Freelist. * @wr: The freelist completion WR in the ingress queue. * @len_to_qid: The lower 32-bits of the first flit of the RSP footer * @iq_handler: Caller's handler for this completion. * @priv: Private pointer of caller * */ static inline void csio_wr_process_fl(struct csio_hw *hw, struct csio_q *q, void *wr, uint32_t len_to_qid, void (*iq_handler)(struct csio_hw *, void *, uint32_t, struct csio_fl_dma_buf *, void *), void *priv) { … } /* * csio_is_new_iqwr - Is this a new Ingress queue entry ? * @q: Ingress quueue. * @ftr: Ingress queue WR SGE footer. * * The entry is new if our generation bit matches the corresponding * bit in the footer of the current WR. */ static inline bool csio_is_new_iqwr(struct csio_q *q, struct csio_iqwr_footer *ftr) { … } /* * csio_wr_process_iq - Process elements in Ingress queue. * @hw: HW pointer * @qidx: Index of queue * @iq_handler: Handler for this queue * @priv: Caller's private pointer * * This routine walks through every entry of the ingress queue, calling * the provided iq_handler with the entry, until the generation bit * flips. */ int csio_wr_process_iq(struct csio_hw *hw, struct csio_q *q, void (*iq_handler)(struct csio_hw *, void *, uint32_t, struct csio_fl_dma_buf *, void *), void *priv) { … } int csio_wr_process_iq_idx(struct csio_hw *hw, int qidx, void (*iq_handler)(struct csio_hw *, void *, uint32_t, struct csio_fl_dma_buf *, void *), void *priv) { … } static int csio_closest_timer(struct csio_sge *s, int time) { … } static int csio_closest_thresh(struct csio_sge *s, int cnt) { … } static void csio_wr_fixup_host_params(struct csio_hw *hw) { … } static void csio_init_intr_coalesce_parms(struct csio_hw *hw) { … } /* * csio_wr_get_sge - Get SGE register values. * @hw: HW module. * * Used by non-master functions and by master-functions relying on config file. */ static void csio_wr_get_sge(struct csio_hw *hw) { … } /* * csio_wr_set_sge - Initialize SGE registers * @hw: HW module. * * Used by Master function to initialize SGE registers in the absence * of a config file. */ static void csio_wr_set_sge(struct csio_hw *hw) { … } void csio_wr_sge_init(struct csio_hw *hw) { … } /* * csio_wrm_init - Initialize Work request module. * @wrm: WR module * @hw: HW pointer * * Allocates memory for an array of queue pointers starting at q_arr. */ int csio_wrm_init(struct csio_wrm *wrm, struct csio_hw *hw) { … } /* * csio_wrm_exit - Initialize Work request module. * @wrm: WR module * @hw: HW module * * Uninitialize WR module. Free q_arr and pointers in it. * We have the additional job of freeing the DMA memory associated * with the queues. */ void csio_wrm_exit(struct csio_wrm *wrm, struct csio_hw *hw) { … }