/*
* 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.
*/
#include <bfa.h>
BFA_TRC_FILE(HAL, SGPG);
BFA_MODULE(sgpg);
/**
* bfa_sgpg_mod BFA SGPG Mode module
*/
/**
* Compute and return memory needed by FCP(im) module.
*/
static void
bfa_sgpg_meminfo(struct bfa_iocfc_cfg_s *cfg, u32 *km_len,
u32 *dm_len)
{
if (cfg->drvcfg.num_sgpgs < BFA_SGPG_MIN)
cfg->drvcfg.num_sgpgs = BFA_SGPG_MIN;
*km_len += (cfg->drvcfg.num_sgpgs + 1) * sizeof(struct bfa_sgpg_s);
*dm_len += (cfg->drvcfg.num_sgpgs + 1) * sizeof(struct bfi_sgpg_s);
}
static void
bfa_sgpg_attach(struct bfa_s *bfa, void *bfad, struct bfa_iocfc_cfg_s *cfg,
struct bfa_meminfo_s *minfo, struct bfa_pcidev_s *pcidev)
{
struct bfa_sgpg_mod_s *mod = BFA_SGPG_MOD(bfa);
int i;
struct bfa_sgpg_s *hsgpg;
struct bfi_sgpg_s *sgpg;
u64 align_len;
union {
u64 pa;
union bfi_addr_u addr;
} sgpg_pa;
INIT_LIST_HEAD(&mod->sgpg_q);
INIT_LIST_HEAD(&mod->sgpg_wait_q);
bfa_trc(bfa, cfg->drvcfg.num_sgpgs);
mod->num_sgpgs = cfg->drvcfg.num_sgpgs;
mod->sgpg_arr_pa = bfa_meminfo_dma_phys(minfo);
align_len = (BFA_SGPG_ROUNDUP(mod->sgpg_arr_pa) - mod->sgpg_arr_pa);
mod->sgpg_arr_pa += align_len;
mod->hsgpg_arr = (struct bfa_sgpg_s *) (bfa_meminfo_kva(minfo) +
align_len);
mod->sgpg_arr = (struct bfi_sgpg_s *) (bfa_meminfo_dma_virt(minfo) +
align_len);
hsgpg = mod->hsgpg_arr;
sgpg = mod->sgpg_arr;
sgpg_pa.pa = mod->sgpg_arr_pa;
mod->free_sgpgs = mod->num_sgpgs;
bfa_assert(!(sgpg_pa.pa & (sizeof(struct bfi_sgpg_s) - 1)));
for (i = 0; i < mod->num_sgpgs; i++) {
bfa_os_memset(hsgpg, 0, sizeof(*hsgpg));
bfa_os_memset(sgpg, 0, sizeof(*sgpg));
hsgpg->sgpg = sgpg;
hsgpg->sgpg_pa = sgpg_pa.addr;
list_add_tail(&hsgpg->qe, &mod->sgpg_q);
hsgpg++;
sgpg++;
sgpg_pa.pa += sizeof(struct bfi_sgpg_s);
}
bfa_meminfo_kva(minfo) = (u8 *) hsgpg;
bfa_meminfo_dma_virt(minfo) = (u8 *) sgpg;
bfa_meminfo_dma_phys(minfo) = sgpg_pa.pa;
}
static void
bfa_sgpg_detach(struct bfa_s *bfa)
{
}
static void
bfa_sgpg_start(struct bfa_s *bfa)
{
}
static void
bfa_sgpg_stop(struct bfa_s *bfa)
{
}
static void
bfa_sgpg_iocdisable(struct bfa_s *bfa)
{
}
/**
* bfa_sgpg_public BFA SGPG public functions
*/
bfa_status_t
bfa_sgpg_malloc(struct bfa_s *bfa, struct list_head *sgpg_q, int nsgpgs)
{
struct bfa_sgpg_mod_s *mod = BFA_SGPG_MOD(bfa);
struct bfa_sgpg_s *hsgpg;
int i;
bfa_trc_fp(bfa, nsgpgs);
if (mod->free_sgpgs < nsgpgs)
return BFA_STATUS_ENOMEM;
for (i = 0; i < nsgpgs; i++) {
bfa_q_deq(&mod->sgpg_q, &hsgpg);
bfa_assert(hsgpg);
list_add_tail(&hsgpg->qe, sgpg_q);
}
mod->free_sgpgs -= nsgpgs;
return BFA_STATUS_OK;
}
void
bfa_sgpg_mfree(struct bfa_s *bfa, struct list_head *sgpg_q, int nsgpg)
{
struct bfa_sgpg_mod_s *mod = BFA_SGPG_MOD(bfa);
struct bfa_sgpg_wqe_s *wqe;
bfa_trc_fp(bfa, nsgpg);
mod->free_sgpgs += nsgpg;
bfa_assert(mod->free_sgpgs <= mod->num_sgpgs);
list_splice_tail_init(sgpg_q, &mod->sgpg_q);
if (list_empty(&mod->sgpg_wait_q))
return;
/**
* satisfy as many waiting requests as possible
*/
do {
wqe = bfa_q_first(&mod->sgpg_wait_q);
if (mod->free_sgpgs < wqe->nsgpg)
nsgpg = mod->free_sgpgs;
else
nsgpg = wqe->nsgpg;
bfa_sgpg_malloc(bfa, &wqe->sgpg_q, nsgpg);
wqe->nsgpg -= nsgpg;
if (wqe->nsgpg == 0) {
list_del(&wqe->qe);
wqe->cbfn(wqe->cbarg);
}
} while (mod->free_sgpgs && !list_empty(&mod->sgpg_wait_q));
}
void
bfa_sgpg_wait(struct bfa_s *bfa, struct bfa_sgpg_wqe_s *wqe, int nsgpg)
{
struct bfa_sgpg_mod_s *mod = BFA_SGPG_MOD(bfa);
bfa_assert(nsgpg > 0);
bfa_assert(nsgpg > mod->free_sgpgs);
wqe->nsgpg_total = wqe->nsgpg = nsgpg;
/**
* allocate any left to this one first
*/
if (mod->free_sgpgs) {
/**
* no one else is waiting for SGPG
*/
bfa_assert(list_empty(&mod->sgpg_wait_q));
list_splice_tail_init(&mod->sgpg_q, &wqe->sgpg_q);
wqe->nsgpg -= mod->free_sgpgs;
mod->free_sgpgs = 0;
}
list_add_tail(&wqe->qe, &mod->sgpg_wait_q);
}
void
bfa_sgpg_wcancel(struct bfa_s *bfa, struct bfa_sgpg_wqe_s *wqe)
{
struct bfa_sgpg_mod_s *mod = BFA_SGPG_MOD(bfa);
bfa_assert(bfa_q_is_on_q(&mod->sgpg_wait_q, wqe));
list_del(&wqe->qe);
if (wqe->nsgpg_total != wqe->nsgpg)
bfa_sgpg_mfree(bfa, &wqe->sgpg_q,
wqe->nsgpg_total - wqe->nsgpg);
}
void
bfa_sgpg_winit(struct bfa_sgpg_wqe_s *wqe, void (*cbfn) (void *cbarg),
void *cbarg)
{
INIT_LIST_HEAD(&wqe->sgpg_q);
wqe->cbfn = cbfn;
wqe->cbarg = cbarg;
}