227 lines
		
	
	
	
		
			4.9 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
		
		
			
		
	
	
			227 lines
		
	
	
	
		
			4.9 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
|   | /*
 | ||
|  |  * 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; | ||
|  | } | ||
|  | 
 | ||
|  | 
 |