| /* |
| * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. |
| * All rights reserved |
| * www.brocade.com |
| * |
| * Linux driver for Brocade Fibre Channel Host Bus Adapter. |
| * |
| * This program is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License (GPL) Version 2 as |
| * published by the Free Software Foundation |
| * |
| * This program is distributed in the hope that it will be useful, but |
| * WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * General Public License for more details. |
| */ |
| |
| /** |
| * bfa_uf.c BFA unsolicited frame receive implementation |
| */ |
| |
| #include <bfa.h> |
| #include <bfa_svc.h> |
| #include <bfi/bfi_uf.h> |
| #include <cs/bfa_debug.h> |
| |
| BFA_TRC_FILE(HAL, UF); |
| BFA_MODULE(uf); |
| |
| /* |
| ***************************************************************************** |
| * Internal functions |
| ***************************************************************************** |
| */ |
| static void |
| __bfa_cb_uf_recv(void *cbarg, bfa_boolean_t complete) |
| { |
| struct bfa_uf_s *uf = cbarg; |
| struct bfa_uf_mod_s *ufm = BFA_UF_MOD(uf->bfa); |
| |
| if (complete) |
| ufm->ufrecv(ufm->cbarg, uf); |
| } |
| |
| static void |
| claim_uf_pbs(struct bfa_uf_mod_s *ufm, struct bfa_meminfo_s *mi) |
| { |
| u32 uf_pb_tot_sz; |
| |
| ufm->uf_pbs_kva = (struct bfa_uf_buf_s *) bfa_meminfo_dma_virt(mi); |
| ufm->uf_pbs_pa = bfa_meminfo_dma_phys(mi); |
| uf_pb_tot_sz = BFA_ROUNDUP((sizeof(struct bfa_uf_buf_s) * ufm->num_ufs), |
| BFA_DMA_ALIGN_SZ); |
| |
| bfa_meminfo_dma_virt(mi) += uf_pb_tot_sz; |
| bfa_meminfo_dma_phys(mi) += uf_pb_tot_sz; |
| |
| bfa_os_memset((void *)ufm->uf_pbs_kva, 0, uf_pb_tot_sz); |
| } |
| |
| static void |
| claim_uf_post_msgs(struct bfa_uf_mod_s *ufm, struct bfa_meminfo_s *mi) |
| { |
| struct bfi_uf_buf_post_s *uf_bp_msg; |
| struct bfi_sge_s *sge; |
| union bfi_addr_u sga_zero = { {0} }; |
| u16 i; |
| u16 buf_len; |
| |
| ufm->uf_buf_posts = (struct bfi_uf_buf_post_s *) bfa_meminfo_kva(mi); |
| uf_bp_msg = ufm->uf_buf_posts; |
| |
| for (i = 0, uf_bp_msg = ufm->uf_buf_posts; i < ufm->num_ufs; |
| i++, uf_bp_msg++) { |
| bfa_os_memset(uf_bp_msg, 0, sizeof(struct bfi_uf_buf_post_s)); |
| |
| uf_bp_msg->buf_tag = i; |
| buf_len = sizeof(struct bfa_uf_buf_s); |
| uf_bp_msg->buf_len = bfa_os_htons(buf_len); |
| bfi_h2i_set(uf_bp_msg->mh, BFI_MC_UF, BFI_UF_H2I_BUF_POST, |
| bfa_lpuid(ufm->bfa)); |
| |
| sge = uf_bp_msg->sge; |
| sge[0].sg_len = buf_len; |
| sge[0].flags = BFI_SGE_DATA_LAST; |
| bfa_dma_addr_set(sge[0].sga, ufm_pbs_pa(ufm, i)); |
| bfa_sge_to_be(sge); |
| |
| sge[1].sg_len = buf_len; |
| sge[1].flags = BFI_SGE_PGDLEN; |
| sge[1].sga = sga_zero; |
| bfa_sge_to_be(&sge[1]); |
| } |
| |
| /** |
| * advance pointer beyond consumed memory |
| */ |
| bfa_meminfo_kva(mi) = (u8 *) uf_bp_msg; |
| } |
| |
| static void |
| claim_ufs(struct bfa_uf_mod_s *ufm, struct bfa_meminfo_s *mi) |
| { |
| u16 i; |
| struct bfa_uf_s *uf; |
| |
| /* |
| * Claim block of memory for UF list |
| */ |
| ufm->uf_list = (struct bfa_uf_s *) bfa_meminfo_kva(mi); |
| |
| /* |
| * Initialize UFs and queue it in UF free queue |
| */ |
| for (i = 0, uf = ufm->uf_list; i < ufm->num_ufs; i++, uf++) { |
| bfa_os_memset(uf, 0, sizeof(struct bfa_uf_s)); |
| uf->bfa = ufm->bfa; |
| uf->uf_tag = i; |
| uf->pb_len = sizeof(struct bfa_uf_buf_s); |
| uf->buf_kva = (void *)&ufm->uf_pbs_kva[i]; |
| uf->buf_pa = ufm_pbs_pa(ufm, i); |
| list_add_tail(&uf->qe, &ufm->uf_free_q); |
| } |
| |
| /** |
| * advance memory pointer |
| */ |
| bfa_meminfo_kva(mi) = (u8 *) uf; |
| } |
| |
| static void |
| uf_mem_claim(struct bfa_uf_mod_s *ufm, struct bfa_meminfo_s *mi) |
| { |
| claim_uf_pbs(ufm, mi); |
| claim_ufs(ufm, mi); |
| claim_uf_post_msgs(ufm, mi); |
| } |
| |
| static void |
| bfa_uf_meminfo(struct bfa_iocfc_cfg_s *cfg, u32 *ndm_len, u32 *dm_len) |
| { |
| u32 num_ufs = cfg->fwcfg.num_uf_bufs; |
| |
| /* |
| * dma-able memory for UF posted bufs |
| */ |
| *dm_len += BFA_ROUNDUP((sizeof(struct bfa_uf_buf_s) * num_ufs), |
| BFA_DMA_ALIGN_SZ); |
| |
| /* |
| * kernel Virtual memory for UFs and UF buf post msg copies |
| */ |
| *ndm_len += sizeof(struct bfa_uf_s) * num_ufs; |
| *ndm_len += sizeof(struct bfi_uf_buf_post_s) * num_ufs; |
| } |
| |
| static void |
| bfa_uf_attach(struct bfa_s *bfa, void *bfad, struct bfa_iocfc_cfg_s *cfg, |
| struct bfa_meminfo_s *meminfo, struct bfa_pcidev_s *pcidev) |
| { |
| struct bfa_uf_mod_s *ufm = BFA_UF_MOD(bfa); |
| |
| bfa_os_memset(ufm, 0, sizeof(struct bfa_uf_mod_s)); |
| ufm->bfa = bfa; |
| ufm->num_ufs = cfg->fwcfg.num_uf_bufs; |
| INIT_LIST_HEAD(&ufm->uf_free_q); |
| INIT_LIST_HEAD(&ufm->uf_posted_q); |
| |
| uf_mem_claim(ufm, meminfo); |
| } |
| |
| static void |
| bfa_uf_initdone(struct bfa_s *bfa) |
| { |
| } |
| |
| static void |
| bfa_uf_detach(struct bfa_s *bfa) |
| { |
| } |
| |
| static struct bfa_uf_s * |
| bfa_uf_get(struct bfa_uf_mod_s *uf_mod) |
| { |
| struct bfa_uf_s *uf; |
| |
| bfa_q_deq(&uf_mod->uf_free_q, &uf); |
| return uf; |
| } |
| |
| static void |
| bfa_uf_put(struct bfa_uf_mod_s *uf_mod, struct bfa_uf_s *uf) |
| { |
| list_add_tail(&uf->qe, &uf_mod->uf_free_q); |
| } |
| |
| static bfa_status_t |
| bfa_uf_post(struct bfa_uf_mod_s *ufm, struct bfa_uf_s *uf) |
| { |
| struct bfi_uf_buf_post_s *uf_post_msg; |
| |
| uf_post_msg = bfa_reqq_next(ufm->bfa, BFA_REQQ_FCXP); |
| if (!uf_post_msg) |
| return BFA_STATUS_FAILED; |
| |
| bfa_os_memcpy(uf_post_msg, &ufm->uf_buf_posts[uf->uf_tag], |
| sizeof(struct bfi_uf_buf_post_s)); |
| bfa_reqq_produce(ufm->bfa, BFA_REQQ_FCXP); |
| |
| bfa_trc(ufm->bfa, uf->uf_tag); |
| |
| list_add_tail(&uf->qe, &ufm->uf_posted_q); |
| return BFA_STATUS_OK; |
| } |
| |
| static void |
| bfa_uf_post_all(struct bfa_uf_mod_s *uf_mod) |
| { |
| struct bfa_uf_s *uf; |
| |
| while ((uf = bfa_uf_get(uf_mod)) != NULL) { |
| if (bfa_uf_post(uf_mod, uf) != BFA_STATUS_OK) |
| break; |
| } |
| } |
| |
| static void |
| uf_recv(struct bfa_s *bfa, struct bfi_uf_frm_rcvd_s *m) |
| { |
| struct bfa_uf_mod_s *ufm = BFA_UF_MOD(bfa); |
| u16 uf_tag = m->buf_tag; |
| struct bfa_uf_buf_s *uf_buf = &ufm->uf_pbs_kva[uf_tag]; |
| struct bfa_uf_s *uf = &ufm->uf_list[uf_tag]; |
| u8 *buf = &uf_buf->d[0]; |
| struct fchs_s *fchs; |
| |
| m->frm_len = bfa_os_ntohs(m->frm_len); |
| m->xfr_len = bfa_os_ntohs(m->xfr_len); |
| |
| fchs = (struct fchs_s *) uf_buf; |
| |
| list_del(&uf->qe); /* dequeue from posted queue */ |
| |
| uf->data_ptr = buf; |
| uf->data_len = m->xfr_len; |
| |
| bfa_assert(uf->data_len >= sizeof(struct fchs_s)); |
| |
| if (uf->data_len == sizeof(struct fchs_s)) { |
| bfa_plog_fchdr(bfa->plog, BFA_PL_MID_HAL_UF, BFA_PL_EID_RX, |
| uf->data_len, (struct fchs_s *) buf); |
| } else { |
| u32 pld_w0 = *((u32 *) (buf + sizeof(struct fchs_s))); |
| bfa_plog_fchdr_and_pl(bfa->plog, BFA_PL_MID_HAL_UF, |
| BFA_PL_EID_RX, uf->data_len, |
| (struct fchs_s *) buf, pld_w0); |
| } |
| |
| bfa_cb_queue(bfa, &uf->hcb_qe, __bfa_cb_uf_recv, uf); |
| } |
| |
| static void |
| bfa_uf_stop(struct bfa_s *bfa) |
| { |
| } |
| |
| static void |
| bfa_uf_iocdisable(struct bfa_s *bfa) |
| { |
| struct bfa_uf_mod_s *ufm = BFA_UF_MOD(bfa); |
| struct bfa_uf_s *uf; |
| struct list_head *qe, *qen; |
| |
| list_for_each_safe(qe, qen, &ufm->uf_posted_q) { |
| uf = (struct bfa_uf_s *) qe; |
| list_del(&uf->qe); |
| bfa_uf_put(ufm, uf); |
| } |
| } |
| |
| static void |
| bfa_uf_start(struct bfa_s *bfa) |
| { |
| bfa_uf_post_all(BFA_UF_MOD(bfa)); |
| } |
| |
| |
| |
| /** |
| * bfa_uf_api |
| */ |
| |
| /** |
| * Register handler for all unsolicted recieve frames. |
| * |
| * @param[in] bfa BFA instance |
| * @param[in] ufrecv receive handler function |
| * @param[in] cbarg receive handler arg |
| */ |
| void |
| bfa_uf_recv_register(struct bfa_s *bfa, bfa_cb_uf_recv_t ufrecv, void *cbarg) |
| { |
| struct bfa_uf_mod_s *ufm = BFA_UF_MOD(bfa); |
| |
| ufm->ufrecv = ufrecv; |
| ufm->cbarg = cbarg; |
| } |
| |
| /** |
| * Free an unsolicited frame back to BFA. |
| * |
| * @param[in] uf unsolicited frame to be freed |
| * |
| * @return None |
| */ |
| void |
| bfa_uf_free(struct bfa_uf_s *uf) |
| { |
| bfa_uf_put(BFA_UF_MOD(uf->bfa), uf); |
| bfa_uf_post_all(BFA_UF_MOD(uf->bfa)); |
| } |
| |
| |
| |
| /** |
| * uf_pub BFA uf module public functions |
| */ |
| |
| void |
| bfa_uf_isr(struct bfa_s *bfa, struct bfi_msg_s *msg) |
| { |
| bfa_trc(bfa, msg->mhdr.msg_id); |
| |
| switch (msg->mhdr.msg_id) { |
| case BFI_UF_I2H_FRM_RCVD: |
| uf_recv(bfa, (struct bfi_uf_frm_rcvd_s *) msg); |
| break; |
| |
| default: |
| bfa_trc(bfa, msg->mhdr.msg_id); |
| bfa_assert(0); |
| } |
| } |
| |
| |