162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * linux/drivers/scsi/esas2r/esas2r_io.c 362306a36Sopenharmony_ci * For use with ATTO ExpressSAS R6xx SAS/SATA RAID controllers 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2001-2013 ATTO Technology, Inc. 662306a36Sopenharmony_ci * (mailto:linuxdrivers@attotech.com)mpt3sas/mpt3sas_trigger_diag. 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * This program is free software; you can redistribute it and/or 962306a36Sopenharmony_ci * modify it under the terms of the GNU General Public License 1062306a36Sopenharmony_ci * as published by the Free Software Foundation; either version 2 1162306a36Sopenharmony_ci * of the License, or (at your option) any later version. 1262306a36Sopenharmony_ci * 1362306a36Sopenharmony_ci * This program is distributed in the hope that it will be useful, 1462306a36Sopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of 1562306a36Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1662306a36Sopenharmony_ci * GNU General Public License for more details. 1762306a36Sopenharmony_ci * 1862306a36Sopenharmony_ci * NO WARRANTY 1962306a36Sopenharmony_ci * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR 2062306a36Sopenharmony_ci * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT 2162306a36Sopenharmony_ci * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, 2262306a36Sopenharmony_ci * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is 2362306a36Sopenharmony_ci * solely responsible for determining the appropriateness of using and 2462306a36Sopenharmony_ci * distributing the Program and assumes all risks associated with its 2562306a36Sopenharmony_ci * exercise of rights under this Agreement, including but not limited to 2662306a36Sopenharmony_ci * the risks and costs of program errors, damage to or loss of data, 2762306a36Sopenharmony_ci * programs or equipment, and unavailability or interruption of operations. 2862306a36Sopenharmony_ci * 2962306a36Sopenharmony_ci * DISCLAIMER OF LIABILITY 3062306a36Sopenharmony_ci * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY 3162306a36Sopenharmony_ci * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 3262306a36Sopenharmony_ci * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND 3362306a36Sopenharmony_ci * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 3462306a36Sopenharmony_ci * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE 3562306a36Sopenharmony_ci * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED 3662306a36Sopenharmony_ci * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES 3762306a36Sopenharmony_ci * 3862306a36Sopenharmony_ci * You should have received a copy of the GNU General Public License 3962306a36Sopenharmony_ci * along with this program; if not, write to the Free Software 4062306a36Sopenharmony_ci * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 4162306a36Sopenharmony_ci * USA. 4262306a36Sopenharmony_ci */ 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci#include "esas2r.h" 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_civoid esas2r_start_request(struct esas2r_adapter *a, struct esas2r_request *rq) 4762306a36Sopenharmony_ci{ 4862306a36Sopenharmony_ci struct esas2r_target *t = NULL; 4962306a36Sopenharmony_ci struct esas2r_request *startrq = rq; 5062306a36Sopenharmony_ci unsigned long flags; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci if (unlikely(test_bit(AF_DEGRADED_MODE, &a->flags) || 5362306a36Sopenharmony_ci test_bit(AF_POWER_DOWN, &a->flags))) { 5462306a36Sopenharmony_ci if (rq->vrq->scsi.function == VDA_FUNC_SCSI) 5562306a36Sopenharmony_ci rq->req_stat = RS_SEL2; 5662306a36Sopenharmony_ci else 5762306a36Sopenharmony_ci rq->req_stat = RS_DEGRADED; 5862306a36Sopenharmony_ci } else if (likely(rq->vrq->scsi.function == VDA_FUNC_SCSI)) { 5962306a36Sopenharmony_ci t = a->targetdb + rq->target_id; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci if (unlikely(t >= a->targetdb_end 6262306a36Sopenharmony_ci || !(t->flags & TF_USED))) { 6362306a36Sopenharmony_ci rq->req_stat = RS_SEL; 6462306a36Sopenharmony_ci } else { 6562306a36Sopenharmony_ci /* copy in the target ID. */ 6662306a36Sopenharmony_ci rq->vrq->scsi.target_id = cpu_to_le16(t->virt_targ_id); 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci /* 6962306a36Sopenharmony_ci * Test if we want to report RS_SEL for missing target. 7062306a36Sopenharmony_ci * Note that if AF_DISC_PENDING is set than this will 7162306a36Sopenharmony_ci * go on the defer queue. 7262306a36Sopenharmony_ci */ 7362306a36Sopenharmony_ci if (unlikely(t->target_state != TS_PRESENT && 7462306a36Sopenharmony_ci !test_bit(AF_DISC_PENDING, &a->flags))) 7562306a36Sopenharmony_ci rq->req_stat = RS_SEL; 7662306a36Sopenharmony_ci } 7762306a36Sopenharmony_ci } 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci if (unlikely(rq->req_stat != RS_PENDING)) { 8062306a36Sopenharmony_ci esas2r_complete_request(a, rq); 8162306a36Sopenharmony_ci return; 8262306a36Sopenharmony_ci } 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci esas2r_trace("rq=%p", rq); 8562306a36Sopenharmony_ci esas2r_trace("rq->vrq->scsi.handle=%x", rq->vrq->scsi.handle); 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci if (rq->vrq->scsi.function == VDA_FUNC_SCSI) { 8862306a36Sopenharmony_ci esas2r_trace("rq->target_id=%d", rq->target_id); 8962306a36Sopenharmony_ci esas2r_trace("rq->vrq->scsi.flags=%x", rq->vrq->scsi.flags); 9062306a36Sopenharmony_ci } 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci spin_lock_irqsave(&a->queue_lock, flags); 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci if (likely(list_empty(&a->defer_list) && 9562306a36Sopenharmony_ci !test_bit(AF_CHPRST_PENDING, &a->flags) && 9662306a36Sopenharmony_ci !test_bit(AF_FLASHING, &a->flags) && 9762306a36Sopenharmony_ci !test_bit(AF_DISC_PENDING, &a->flags))) 9862306a36Sopenharmony_ci esas2r_local_start_request(a, startrq); 9962306a36Sopenharmony_ci else 10062306a36Sopenharmony_ci list_add_tail(&startrq->req_list, &a->defer_list); 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci spin_unlock_irqrestore(&a->queue_lock, flags); 10362306a36Sopenharmony_ci} 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci/* 10662306a36Sopenharmony_ci * Starts the specified request. all requests have RS_PENDING set when this 10762306a36Sopenharmony_ci * routine is called. The caller is usually esas2r_start_request, but 10862306a36Sopenharmony_ci * esas2r_do_deferred_processes will start request that are deferred. 10962306a36Sopenharmony_ci * 11062306a36Sopenharmony_ci * The caller must ensure that requests can be started. 11162306a36Sopenharmony_ci * 11262306a36Sopenharmony_ci * esas2r_start_request will defer a request if there are already requests 11362306a36Sopenharmony_ci * waiting or there is a chip reset pending. once the reset condition clears, 11462306a36Sopenharmony_ci * esas2r_do_deferred_processes will call this function to start the request. 11562306a36Sopenharmony_ci * 11662306a36Sopenharmony_ci * When a request is started, it is placed on the active list and queued to 11762306a36Sopenharmony_ci * the controller. 11862306a36Sopenharmony_ci */ 11962306a36Sopenharmony_civoid esas2r_local_start_request(struct esas2r_adapter *a, 12062306a36Sopenharmony_ci struct esas2r_request *rq) 12162306a36Sopenharmony_ci{ 12262306a36Sopenharmony_ci esas2r_trace_enter(); 12362306a36Sopenharmony_ci esas2r_trace("rq=%p", rq); 12462306a36Sopenharmony_ci esas2r_trace("rq->vrq:%p", rq->vrq); 12562306a36Sopenharmony_ci esas2r_trace("rq->vrq_md->phys_addr:%x", rq->vrq_md->phys_addr); 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci if (unlikely(rq->vrq->scsi.function == VDA_FUNC_FLASH 12862306a36Sopenharmony_ci && rq->vrq->flash.sub_func == VDA_FLASH_COMMIT)) 12962306a36Sopenharmony_ci set_bit(AF_FLASHING, &a->flags); 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci list_add_tail(&rq->req_list, &a->active_list); 13262306a36Sopenharmony_ci esas2r_start_vda_request(a, rq); 13362306a36Sopenharmony_ci esas2r_trace_exit(); 13462306a36Sopenharmony_ci return; 13562306a36Sopenharmony_ci} 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_civoid esas2r_start_vda_request(struct esas2r_adapter *a, 13862306a36Sopenharmony_ci struct esas2r_request *rq) 13962306a36Sopenharmony_ci{ 14062306a36Sopenharmony_ci struct esas2r_inbound_list_source_entry *element; 14162306a36Sopenharmony_ci u32 dw; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci rq->req_stat = RS_STARTED; 14462306a36Sopenharmony_ci /* 14562306a36Sopenharmony_ci * Calculate the inbound list entry location and the current state of 14662306a36Sopenharmony_ci * toggle bit. 14762306a36Sopenharmony_ci */ 14862306a36Sopenharmony_ci a->last_write++; 14962306a36Sopenharmony_ci if (a->last_write >= a->list_size) { 15062306a36Sopenharmony_ci a->last_write = 0; 15162306a36Sopenharmony_ci /* update the toggle bit */ 15262306a36Sopenharmony_ci if (test_bit(AF_COMM_LIST_TOGGLE, &a->flags)) 15362306a36Sopenharmony_ci clear_bit(AF_COMM_LIST_TOGGLE, &a->flags); 15462306a36Sopenharmony_ci else 15562306a36Sopenharmony_ci set_bit(AF_COMM_LIST_TOGGLE, &a->flags); 15662306a36Sopenharmony_ci } 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci element = 15962306a36Sopenharmony_ci (struct esas2r_inbound_list_source_entry *)a->inbound_list_md. 16062306a36Sopenharmony_ci virt_addr 16162306a36Sopenharmony_ci + a->last_write; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci /* Set the VDA request size if it was never modified */ 16462306a36Sopenharmony_ci if (rq->vda_req_sz == RQ_SIZE_DEFAULT) 16562306a36Sopenharmony_ci rq->vda_req_sz = (u16)(a->max_vdareq_size / sizeof(u32)); 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci element->address = cpu_to_le64(rq->vrq_md->phys_addr); 16862306a36Sopenharmony_ci element->length = cpu_to_le32(rq->vda_req_sz); 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci /* Update the write pointer */ 17162306a36Sopenharmony_ci dw = a->last_write; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci if (test_bit(AF_COMM_LIST_TOGGLE, &a->flags)) 17462306a36Sopenharmony_ci dw |= MU_ILW_TOGGLE; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci esas2r_trace("rq->vrq->scsi.handle:%x", rq->vrq->scsi.handle); 17762306a36Sopenharmony_ci esas2r_trace("dw:%x", dw); 17862306a36Sopenharmony_ci esas2r_trace("rq->vda_req_sz:%x", rq->vda_req_sz); 17962306a36Sopenharmony_ci esas2r_write_register_dword(a, MU_IN_LIST_WRITE, dw); 18062306a36Sopenharmony_ci} 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci/* 18362306a36Sopenharmony_ci * Build the scatter/gather list for an I/O request according to the 18462306a36Sopenharmony_ci * specifications placed in the s/g context. The caller must initialize 18562306a36Sopenharmony_ci * context prior to the initial call by calling esas2r_sgc_init(). 18662306a36Sopenharmony_ci */ 18762306a36Sopenharmony_cibool esas2r_build_sg_list_sge(struct esas2r_adapter *a, 18862306a36Sopenharmony_ci struct esas2r_sg_context *sgc) 18962306a36Sopenharmony_ci{ 19062306a36Sopenharmony_ci struct esas2r_request *rq = sgc->first_req; 19162306a36Sopenharmony_ci union atto_vda_req *vrq = rq->vrq; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci while (sgc->length) { 19462306a36Sopenharmony_ci u32 rem = 0; 19562306a36Sopenharmony_ci u64 addr; 19662306a36Sopenharmony_ci u32 len; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci len = (*sgc->get_phys_addr)(sgc, &addr); 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci if (unlikely(len == 0)) 20162306a36Sopenharmony_ci return false; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci /* if current length is more than what's left, stop there */ 20462306a36Sopenharmony_ci if (unlikely(len > sgc->length)) 20562306a36Sopenharmony_ci len = sgc->length; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_cianother_entry: 20862306a36Sopenharmony_ci /* limit to a round number less than the maximum length */ 20962306a36Sopenharmony_ci if (len > SGE_LEN_MAX) { 21062306a36Sopenharmony_ci /* 21162306a36Sopenharmony_ci * Save the remainder of the split. Whenever we limit 21262306a36Sopenharmony_ci * an entry we come back around to build entries out 21362306a36Sopenharmony_ci * of the leftover. We do this to prevent multiple 21462306a36Sopenharmony_ci * calls to the get_phys_addr() function for an SGE 21562306a36Sopenharmony_ci * that is too large. 21662306a36Sopenharmony_ci */ 21762306a36Sopenharmony_ci rem = len - SGE_LEN_MAX; 21862306a36Sopenharmony_ci len = SGE_LEN_MAX; 21962306a36Sopenharmony_ci } 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci /* See if we need to allocate a new SGL */ 22262306a36Sopenharmony_ci if (unlikely(sgc->sge.a64.curr > sgc->sge.a64.limit)) { 22362306a36Sopenharmony_ci u8 sgelen; 22462306a36Sopenharmony_ci struct esas2r_mem_desc *sgl; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci /* 22762306a36Sopenharmony_ci * If no SGls are available, return failure. The 22862306a36Sopenharmony_ci * caller can call us later with the current context 22962306a36Sopenharmony_ci * to pick up here. 23062306a36Sopenharmony_ci */ 23162306a36Sopenharmony_ci sgl = esas2r_alloc_sgl(a); 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci if (unlikely(sgl == NULL)) 23462306a36Sopenharmony_ci return false; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci /* Calculate the length of the last SGE filled in */ 23762306a36Sopenharmony_ci sgelen = (u8)((u8 *)sgc->sge.a64.curr 23862306a36Sopenharmony_ci - (u8 *)sgc->sge.a64.last); 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci /* 24162306a36Sopenharmony_ci * Copy the last SGE filled in to the first entry of 24262306a36Sopenharmony_ci * the new SGL to make room for the chain entry. 24362306a36Sopenharmony_ci */ 24462306a36Sopenharmony_ci memcpy(sgl->virt_addr, sgc->sge.a64.last, sgelen); 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci /* Figure out the new curr pointer in the new segment */ 24762306a36Sopenharmony_ci sgc->sge.a64.curr = 24862306a36Sopenharmony_ci (struct atto_vda_sge *)((u8 *)sgl->virt_addr + 24962306a36Sopenharmony_ci sgelen); 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci /* Set the limit pointer and build the chain entry */ 25262306a36Sopenharmony_ci sgc->sge.a64.limit = 25362306a36Sopenharmony_ci (struct atto_vda_sge *)((u8 *)sgl->virt_addr 25462306a36Sopenharmony_ci + sgl_page_size 25562306a36Sopenharmony_ci - sizeof(struct 25662306a36Sopenharmony_ci atto_vda_sge)); 25762306a36Sopenharmony_ci sgc->sge.a64.last->length = cpu_to_le32( 25862306a36Sopenharmony_ci SGE_CHAIN | SGE_ADDR_64); 25962306a36Sopenharmony_ci sgc->sge.a64.last->address = 26062306a36Sopenharmony_ci cpu_to_le64(sgl->phys_addr); 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci /* 26362306a36Sopenharmony_ci * Now, if there was a previous chain entry, then 26462306a36Sopenharmony_ci * update it to contain the length of this segment 26562306a36Sopenharmony_ci * and size of this chain. otherwise this is the 26662306a36Sopenharmony_ci * first SGL, so set the chain_offset in the request. 26762306a36Sopenharmony_ci */ 26862306a36Sopenharmony_ci if (sgc->sge.a64.chain) { 26962306a36Sopenharmony_ci sgc->sge.a64.chain->length |= 27062306a36Sopenharmony_ci cpu_to_le32( 27162306a36Sopenharmony_ci ((u8 *)(sgc->sge.a64. 27262306a36Sopenharmony_ci last + 1) 27362306a36Sopenharmony_ci - (u8 *)rq->sg_table-> 27462306a36Sopenharmony_ci virt_addr) 27562306a36Sopenharmony_ci + sizeof(struct atto_vda_sge) * 27662306a36Sopenharmony_ci LOBIT(SGE_CHAIN_SZ)); 27762306a36Sopenharmony_ci } else { 27862306a36Sopenharmony_ci vrq->scsi.chain_offset = (u8) 27962306a36Sopenharmony_ci ((u8 *)sgc-> 28062306a36Sopenharmony_ci sge.a64.last - 28162306a36Sopenharmony_ci (u8 *)vrq); 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci /* 28462306a36Sopenharmony_ci * This is the first SGL, so set the 28562306a36Sopenharmony_ci * chain_offset and the VDA request size in 28662306a36Sopenharmony_ci * the request. 28762306a36Sopenharmony_ci */ 28862306a36Sopenharmony_ci rq->vda_req_sz = 28962306a36Sopenharmony_ci (vrq->scsi.chain_offset + 29062306a36Sopenharmony_ci sizeof(struct atto_vda_sge) + 29162306a36Sopenharmony_ci 3) 29262306a36Sopenharmony_ci / sizeof(u32); 29362306a36Sopenharmony_ci } 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci /* 29662306a36Sopenharmony_ci * Remember this so when we get a new SGL filled in we 29762306a36Sopenharmony_ci * can update the length of this chain entry. 29862306a36Sopenharmony_ci */ 29962306a36Sopenharmony_ci sgc->sge.a64.chain = sgc->sge.a64.last; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci /* Now link the new SGL onto the primary request. */ 30262306a36Sopenharmony_ci list_add(&sgl->next_desc, &rq->sg_table_head); 30362306a36Sopenharmony_ci } 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci /* Update last one filled in */ 30662306a36Sopenharmony_ci sgc->sge.a64.last = sgc->sge.a64.curr; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci /* Build the new SGE and update the S/G context */ 30962306a36Sopenharmony_ci sgc->sge.a64.curr->length = cpu_to_le32(SGE_ADDR_64 | len); 31062306a36Sopenharmony_ci sgc->sge.a64.curr->address = cpu_to_le32(addr); 31162306a36Sopenharmony_ci sgc->sge.a64.curr++; 31262306a36Sopenharmony_ci sgc->cur_offset += len; 31362306a36Sopenharmony_ci sgc->length -= len; 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci /* 31662306a36Sopenharmony_ci * Check if we previously split an entry. If so we have to 31762306a36Sopenharmony_ci * pick up where we left off. 31862306a36Sopenharmony_ci */ 31962306a36Sopenharmony_ci if (rem) { 32062306a36Sopenharmony_ci addr += len; 32162306a36Sopenharmony_ci len = rem; 32262306a36Sopenharmony_ci rem = 0; 32362306a36Sopenharmony_ci goto another_entry; 32462306a36Sopenharmony_ci } 32562306a36Sopenharmony_ci } 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci /* Mark the end of the SGL */ 32862306a36Sopenharmony_ci sgc->sge.a64.last->length |= cpu_to_le32(SGE_LAST); 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci /* 33162306a36Sopenharmony_ci * If there was a previous chain entry, update the length to indicate 33262306a36Sopenharmony_ci * the length of this last segment. 33362306a36Sopenharmony_ci */ 33462306a36Sopenharmony_ci if (sgc->sge.a64.chain) { 33562306a36Sopenharmony_ci sgc->sge.a64.chain->length |= cpu_to_le32( 33662306a36Sopenharmony_ci ((u8 *)(sgc->sge.a64.curr) - 33762306a36Sopenharmony_ci (u8 *)rq->sg_table->virt_addr)); 33862306a36Sopenharmony_ci } else { 33962306a36Sopenharmony_ci u16 reqsize; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci /* 34262306a36Sopenharmony_ci * The entire VDA request was not used so lets 34362306a36Sopenharmony_ci * set the size of the VDA request to be DMA'd 34462306a36Sopenharmony_ci */ 34562306a36Sopenharmony_ci reqsize = 34662306a36Sopenharmony_ci ((u16)((u8 *)sgc->sge.a64.last - (u8 *)vrq) 34762306a36Sopenharmony_ci + sizeof(struct atto_vda_sge) + 3) / sizeof(u32); 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci /* 35062306a36Sopenharmony_ci * Only update the request size if it is bigger than what is 35162306a36Sopenharmony_ci * already there. We can come in here twice for some management 35262306a36Sopenharmony_ci * commands. 35362306a36Sopenharmony_ci */ 35462306a36Sopenharmony_ci if (reqsize > rq->vda_req_sz) 35562306a36Sopenharmony_ci rq->vda_req_sz = reqsize; 35662306a36Sopenharmony_ci } 35762306a36Sopenharmony_ci return true; 35862306a36Sopenharmony_ci} 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci/* 36262306a36Sopenharmony_ci * Create PRD list for each I-block consumed by the command. This routine 36362306a36Sopenharmony_ci * determines how much data is required from each I-block being consumed 36462306a36Sopenharmony_ci * by the command. The first and last I-blocks can be partials and all of 36562306a36Sopenharmony_ci * the I-blocks in between are for a full I-block of data. 36662306a36Sopenharmony_ci * 36762306a36Sopenharmony_ci * The interleave size is used to determine the number of bytes in the 1st 36862306a36Sopenharmony_ci * I-block and the remaining I-blocks are what remeains. 36962306a36Sopenharmony_ci */ 37062306a36Sopenharmony_cistatic bool esas2r_build_prd_iblk(struct esas2r_adapter *a, 37162306a36Sopenharmony_ci struct esas2r_sg_context *sgc) 37262306a36Sopenharmony_ci{ 37362306a36Sopenharmony_ci struct esas2r_request *rq = sgc->first_req; 37462306a36Sopenharmony_ci u64 addr; 37562306a36Sopenharmony_ci u32 len; 37662306a36Sopenharmony_ci struct esas2r_mem_desc *sgl; 37762306a36Sopenharmony_ci u32 numchain = 1; 37862306a36Sopenharmony_ci u32 rem = 0; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci while (sgc->length) { 38162306a36Sopenharmony_ci /* Get the next address/length pair */ 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci len = (*sgc->get_phys_addr)(sgc, &addr); 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci if (unlikely(len == 0)) 38662306a36Sopenharmony_ci return false; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci /* If current length is more than what's left, stop there */ 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci if (unlikely(len > sgc->length)) 39162306a36Sopenharmony_ci len = sgc->length; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_cianother_entry: 39462306a36Sopenharmony_ci /* Limit to a round number less than the maximum length */ 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci if (len > PRD_LEN_MAX) { 39762306a36Sopenharmony_ci /* 39862306a36Sopenharmony_ci * Save the remainder of the split. whenever we limit 39962306a36Sopenharmony_ci * an entry we come back around to build entries out 40062306a36Sopenharmony_ci * of the leftover. We do this to prevent multiple 40162306a36Sopenharmony_ci * calls to the get_phys_addr() function for an SGE 40262306a36Sopenharmony_ci * that is too large. 40362306a36Sopenharmony_ci */ 40462306a36Sopenharmony_ci rem = len - PRD_LEN_MAX; 40562306a36Sopenharmony_ci len = PRD_LEN_MAX; 40662306a36Sopenharmony_ci } 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci /* See if we need to allocate a new SGL */ 40962306a36Sopenharmony_ci if (sgc->sge.prd.sge_cnt == 0) { 41062306a36Sopenharmony_ci if (len == sgc->length) { 41162306a36Sopenharmony_ci /* 41262306a36Sopenharmony_ci * We only have 1 PRD entry left. 41362306a36Sopenharmony_ci * It can be placed where the chain 41462306a36Sopenharmony_ci * entry would have gone 41562306a36Sopenharmony_ci */ 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci /* Build the simple SGE */ 41862306a36Sopenharmony_ci sgc->sge.prd.curr->ctl_len = cpu_to_le32( 41962306a36Sopenharmony_ci PRD_DATA | len); 42062306a36Sopenharmony_ci sgc->sge.prd.curr->address = cpu_to_le64(addr); 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci /* Adjust length related fields */ 42362306a36Sopenharmony_ci sgc->cur_offset += len; 42462306a36Sopenharmony_ci sgc->length -= len; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci /* We use the reserved chain entry for data */ 42762306a36Sopenharmony_ci numchain = 0; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci break; 43062306a36Sopenharmony_ci } 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci if (sgc->sge.prd.chain) { 43362306a36Sopenharmony_ci /* 43462306a36Sopenharmony_ci * Fill # of entries of current SGL in previous 43562306a36Sopenharmony_ci * chain the length of this current SGL may not 43662306a36Sopenharmony_ci * full. 43762306a36Sopenharmony_ci */ 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci sgc->sge.prd.chain->ctl_len |= cpu_to_le32( 44062306a36Sopenharmony_ci sgc->sge.prd.sgl_max_cnt); 44162306a36Sopenharmony_ci } 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci /* 44462306a36Sopenharmony_ci * If no SGls are available, return failure. The 44562306a36Sopenharmony_ci * caller can call us later with the current context 44662306a36Sopenharmony_ci * to pick up here. 44762306a36Sopenharmony_ci */ 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci sgl = esas2r_alloc_sgl(a); 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci if (unlikely(sgl == NULL)) 45262306a36Sopenharmony_ci return false; 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci /* 45562306a36Sopenharmony_ci * Link the new SGL onto the chain 45662306a36Sopenharmony_ci * They are in reverse order 45762306a36Sopenharmony_ci */ 45862306a36Sopenharmony_ci list_add(&sgl->next_desc, &rq->sg_table_head); 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci /* 46162306a36Sopenharmony_ci * An SGL was just filled in and we are starting 46262306a36Sopenharmony_ci * a new SGL. Prime the chain of the ending SGL with 46362306a36Sopenharmony_ci * info that points to the new SGL. The length gets 46462306a36Sopenharmony_ci * filled in when the new SGL is filled or ended 46562306a36Sopenharmony_ci */ 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci sgc->sge.prd.chain = sgc->sge.prd.curr; 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci sgc->sge.prd.chain->ctl_len = cpu_to_le32(PRD_CHAIN); 47062306a36Sopenharmony_ci sgc->sge.prd.chain->address = 47162306a36Sopenharmony_ci cpu_to_le64(sgl->phys_addr); 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci /* 47462306a36Sopenharmony_ci * Start a new segment. 47562306a36Sopenharmony_ci * Take one away and save for chain SGE 47662306a36Sopenharmony_ci */ 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci sgc->sge.prd.curr = 47962306a36Sopenharmony_ci (struct atto_physical_region_description *)sgl 48062306a36Sopenharmony_ci -> 48162306a36Sopenharmony_ci virt_addr; 48262306a36Sopenharmony_ci sgc->sge.prd.sge_cnt = sgc->sge.prd.sgl_max_cnt - 1; 48362306a36Sopenharmony_ci } 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci sgc->sge.prd.sge_cnt--; 48662306a36Sopenharmony_ci /* Build the simple SGE */ 48762306a36Sopenharmony_ci sgc->sge.prd.curr->ctl_len = cpu_to_le32(PRD_DATA | len); 48862306a36Sopenharmony_ci sgc->sge.prd.curr->address = cpu_to_le64(addr); 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci /* Used another element. Point to the next one */ 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci sgc->sge.prd.curr++; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci /* Adjust length related fields */ 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci sgc->cur_offset += len; 49762306a36Sopenharmony_ci sgc->length -= len; 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci /* 50062306a36Sopenharmony_ci * Check if we previously split an entry. If so we have to 50162306a36Sopenharmony_ci * pick up where we left off. 50262306a36Sopenharmony_ci */ 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci if (rem) { 50562306a36Sopenharmony_ci addr += len; 50662306a36Sopenharmony_ci len = rem; 50762306a36Sopenharmony_ci rem = 0; 50862306a36Sopenharmony_ci goto another_entry; 50962306a36Sopenharmony_ci } 51062306a36Sopenharmony_ci } 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci if (!list_empty(&rq->sg_table_head)) { 51362306a36Sopenharmony_ci if (sgc->sge.prd.chain) { 51462306a36Sopenharmony_ci sgc->sge.prd.chain->ctl_len |= 51562306a36Sopenharmony_ci cpu_to_le32(sgc->sge.prd.sgl_max_cnt 51662306a36Sopenharmony_ci - sgc->sge.prd.sge_cnt 51762306a36Sopenharmony_ci - numchain); 51862306a36Sopenharmony_ci } 51962306a36Sopenharmony_ci } 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci return true; 52262306a36Sopenharmony_ci} 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_cibool esas2r_build_sg_list_prd(struct esas2r_adapter *a, 52562306a36Sopenharmony_ci struct esas2r_sg_context *sgc) 52662306a36Sopenharmony_ci{ 52762306a36Sopenharmony_ci struct esas2r_request *rq = sgc->first_req; 52862306a36Sopenharmony_ci u32 len = sgc->length; 52962306a36Sopenharmony_ci struct esas2r_target *t = a->targetdb + rq->target_id; 53062306a36Sopenharmony_ci u8 is_i_o = 0; 53162306a36Sopenharmony_ci u16 reqsize; 53262306a36Sopenharmony_ci struct atto_physical_region_description *curr_iblk_chn; 53362306a36Sopenharmony_ci u8 *cdb = (u8 *)&rq->vrq->scsi.cdb[0]; 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci /* 53662306a36Sopenharmony_ci * extract LBA from command so we can determine 53762306a36Sopenharmony_ci * the I-Block boundary 53862306a36Sopenharmony_ci */ 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci if (rq->vrq->scsi.function == VDA_FUNC_SCSI 54162306a36Sopenharmony_ci && t->target_state == TS_PRESENT 54262306a36Sopenharmony_ci && !(t->flags & TF_PASS_THRU)) { 54362306a36Sopenharmony_ci u32 lbalo = 0; 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci switch (rq->vrq->scsi.cdb[0]) { 54662306a36Sopenharmony_ci case READ_16: 54762306a36Sopenharmony_ci case WRITE_16: 54862306a36Sopenharmony_ci { 54962306a36Sopenharmony_ci lbalo = 55062306a36Sopenharmony_ci MAKEDWORD(MAKEWORD(cdb[9], 55162306a36Sopenharmony_ci cdb[8]), 55262306a36Sopenharmony_ci MAKEWORD(cdb[7], 55362306a36Sopenharmony_ci cdb[6])); 55462306a36Sopenharmony_ci is_i_o = 1; 55562306a36Sopenharmony_ci break; 55662306a36Sopenharmony_ci } 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci case READ_12: 55962306a36Sopenharmony_ci case WRITE_12: 56062306a36Sopenharmony_ci case READ_10: 56162306a36Sopenharmony_ci case WRITE_10: 56262306a36Sopenharmony_ci { 56362306a36Sopenharmony_ci lbalo = 56462306a36Sopenharmony_ci MAKEDWORD(MAKEWORD(cdb[5], 56562306a36Sopenharmony_ci cdb[4]), 56662306a36Sopenharmony_ci MAKEWORD(cdb[3], 56762306a36Sopenharmony_ci cdb[2])); 56862306a36Sopenharmony_ci is_i_o = 1; 56962306a36Sopenharmony_ci break; 57062306a36Sopenharmony_ci } 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci case READ_6: 57362306a36Sopenharmony_ci case WRITE_6: 57462306a36Sopenharmony_ci { 57562306a36Sopenharmony_ci lbalo = 57662306a36Sopenharmony_ci MAKEDWORD(MAKEWORD(cdb[3], 57762306a36Sopenharmony_ci cdb[2]), 57862306a36Sopenharmony_ci MAKEWORD(cdb[1] & 0x1F, 57962306a36Sopenharmony_ci 0)); 58062306a36Sopenharmony_ci is_i_o = 1; 58162306a36Sopenharmony_ci break; 58262306a36Sopenharmony_ci } 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci default: 58562306a36Sopenharmony_ci break; 58662306a36Sopenharmony_ci } 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci if (is_i_o) { 58962306a36Sopenharmony_ci u32 startlba; 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci rq->vrq->scsi.iblk_cnt_prd = 0; 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci /* Determine size of 1st I-block PRD list */ 59462306a36Sopenharmony_ci startlba = t->inter_block - (lbalo & (t->inter_block - 59562306a36Sopenharmony_ci 1)); 59662306a36Sopenharmony_ci sgc->length = startlba * t->block_size; 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci /* Chk if the 1st iblk chain starts at base of Iblock */ 59962306a36Sopenharmony_ci if ((lbalo & (t->inter_block - 1)) == 0) 60062306a36Sopenharmony_ci rq->flags |= RF_1ST_IBLK_BASE; 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci if (sgc->length > len) 60362306a36Sopenharmony_ci sgc->length = len; 60462306a36Sopenharmony_ci } else { 60562306a36Sopenharmony_ci sgc->length = len; 60662306a36Sopenharmony_ci } 60762306a36Sopenharmony_ci } else { 60862306a36Sopenharmony_ci sgc->length = len; 60962306a36Sopenharmony_ci } 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci /* get our starting chain address */ 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci curr_iblk_chn = 61462306a36Sopenharmony_ci (struct atto_physical_region_description *)sgc->sge.a64.curr; 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci sgc->sge.prd.sgl_max_cnt = sgl_page_size / 61762306a36Sopenharmony_ci sizeof(struct 61862306a36Sopenharmony_ci atto_physical_region_description); 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci /* create all of the I-block PRD lists */ 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci while (len) { 62362306a36Sopenharmony_ci sgc->sge.prd.sge_cnt = 0; 62462306a36Sopenharmony_ci sgc->sge.prd.chain = NULL; 62562306a36Sopenharmony_ci sgc->sge.prd.curr = curr_iblk_chn; 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci /* increment to next I-Block */ 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci len -= sgc->length; 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci /* go build the next I-Block PRD list */ 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci if (unlikely(!esas2r_build_prd_iblk(a, sgc))) 63462306a36Sopenharmony_ci return false; 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci curr_iblk_chn++; 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci if (is_i_o) { 63962306a36Sopenharmony_ci rq->vrq->scsi.iblk_cnt_prd++; 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci if (len > t->inter_byte) 64262306a36Sopenharmony_ci sgc->length = t->inter_byte; 64362306a36Sopenharmony_ci else 64462306a36Sopenharmony_ci sgc->length = len; 64562306a36Sopenharmony_ci } 64662306a36Sopenharmony_ci } 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci /* figure out the size used of the VDA request */ 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci reqsize = ((u16)((u8 *)curr_iblk_chn - (u8 *)rq->vrq)) 65162306a36Sopenharmony_ci / sizeof(u32); 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci /* 65462306a36Sopenharmony_ci * only update the request size if it is bigger than what is 65562306a36Sopenharmony_ci * already there. we can come in here twice for some management 65662306a36Sopenharmony_ci * commands. 65762306a36Sopenharmony_ci */ 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci if (reqsize > rq->vda_req_sz) 66062306a36Sopenharmony_ci rq->vda_req_sz = reqsize; 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci return true; 66362306a36Sopenharmony_ci} 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_cistatic void esas2r_handle_pending_reset(struct esas2r_adapter *a, u32 currtime) 66662306a36Sopenharmony_ci{ 66762306a36Sopenharmony_ci u32 delta = currtime - a->chip_init_time; 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci if (delta <= ESAS2R_CHPRST_WAIT_TIME) { 67062306a36Sopenharmony_ci /* Wait before accessing registers */ 67162306a36Sopenharmony_ci } else if (delta >= ESAS2R_CHPRST_TIME) { 67262306a36Sopenharmony_ci /* 67362306a36Sopenharmony_ci * The last reset failed so try again. Reset 67462306a36Sopenharmony_ci * processing will give up after three tries. 67562306a36Sopenharmony_ci */ 67662306a36Sopenharmony_ci esas2r_local_reset_adapter(a); 67762306a36Sopenharmony_ci } else { 67862306a36Sopenharmony_ci /* We can now see if the firmware is ready */ 67962306a36Sopenharmony_ci u32 doorbell; 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci doorbell = esas2r_read_register_dword(a, MU_DOORBELL_OUT); 68262306a36Sopenharmony_ci if (doorbell == 0xFFFFFFFF || !(doorbell & DRBL_FORCE_INT)) { 68362306a36Sopenharmony_ci esas2r_force_interrupt(a); 68462306a36Sopenharmony_ci } else { 68562306a36Sopenharmony_ci u32 ver = (doorbell & DRBL_FW_VER_MSK); 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci /* Driver supports API version 0 and 1 */ 68862306a36Sopenharmony_ci esas2r_write_register_dword(a, MU_DOORBELL_OUT, 68962306a36Sopenharmony_ci doorbell); 69062306a36Sopenharmony_ci if (ver == DRBL_FW_VER_0) { 69162306a36Sopenharmony_ci set_bit(AF_CHPRST_DETECTED, &a->flags); 69262306a36Sopenharmony_ci set_bit(AF_LEGACY_SGE_MODE, &a->flags); 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci a->max_vdareq_size = 128; 69562306a36Sopenharmony_ci a->build_sgl = esas2r_build_sg_list_sge; 69662306a36Sopenharmony_ci } else if (ver == DRBL_FW_VER_1) { 69762306a36Sopenharmony_ci set_bit(AF_CHPRST_DETECTED, &a->flags); 69862306a36Sopenharmony_ci clear_bit(AF_LEGACY_SGE_MODE, &a->flags); 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci a->max_vdareq_size = 1024; 70162306a36Sopenharmony_ci a->build_sgl = esas2r_build_sg_list_prd; 70262306a36Sopenharmony_ci } else { 70362306a36Sopenharmony_ci esas2r_local_reset_adapter(a); 70462306a36Sopenharmony_ci } 70562306a36Sopenharmony_ci } 70662306a36Sopenharmony_ci } 70762306a36Sopenharmony_ci} 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci/* This function must be called once per timer tick */ 71162306a36Sopenharmony_civoid esas2r_timer_tick(struct esas2r_adapter *a) 71262306a36Sopenharmony_ci{ 71362306a36Sopenharmony_ci u32 currtime = jiffies_to_msecs(jiffies); 71462306a36Sopenharmony_ci u32 deltatime = currtime - a->last_tick_time; 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci a->last_tick_time = currtime; 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci /* count down the uptime */ 71962306a36Sopenharmony_ci if (a->chip_uptime && 72062306a36Sopenharmony_ci !test_bit(AF_CHPRST_PENDING, &a->flags) && 72162306a36Sopenharmony_ci !test_bit(AF_DISC_PENDING, &a->flags)) { 72262306a36Sopenharmony_ci if (deltatime >= a->chip_uptime) 72362306a36Sopenharmony_ci a->chip_uptime = 0; 72462306a36Sopenharmony_ci else 72562306a36Sopenharmony_ci a->chip_uptime -= deltatime; 72662306a36Sopenharmony_ci } 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci if (test_bit(AF_CHPRST_PENDING, &a->flags)) { 72962306a36Sopenharmony_ci if (!test_bit(AF_CHPRST_NEEDED, &a->flags) && 73062306a36Sopenharmony_ci !test_bit(AF_CHPRST_DETECTED, &a->flags)) 73162306a36Sopenharmony_ci esas2r_handle_pending_reset(a, currtime); 73262306a36Sopenharmony_ci } else { 73362306a36Sopenharmony_ci if (test_bit(AF_DISC_PENDING, &a->flags)) 73462306a36Sopenharmony_ci esas2r_disc_check_complete(a); 73562306a36Sopenharmony_ci if (test_bit(AF_HEARTBEAT_ENB, &a->flags)) { 73662306a36Sopenharmony_ci if (test_bit(AF_HEARTBEAT, &a->flags)) { 73762306a36Sopenharmony_ci if ((currtime - a->heartbeat_time) >= 73862306a36Sopenharmony_ci ESAS2R_HEARTBEAT_TIME) { 73962306a36Sopenharmony_ci clear_bit(AF_HEARTBEAT, &a->flags); 74062306a36Sopenharmony_ci esas2r_hdebug("heartbeat failed"); 74162306a36Sopenharmony_ci esas2r_log(ESAS2R_LOG_CRIT, 74262306a36Sopenharmony_ci "heartbeat failed"); 74362306a36Sopenharmony_ci esas2r_bugon(); 74462306a36Sopenharmony_ci esas2r_local_reset_adapter(a); 74562306a36Sopenharmony_ci } 74662306a36Sopenharmony_ci } else { 74762306a36Sopenharmony_ci set_bit(AF_HEARTBEAT, &a->flags); 74862306a36Sopenharmony_ci a->heartbeat_time = currtime; 74962306a36Sopenharmony_ci esas2r_force_interrupt(a); 75062306a36Sopenharmony_ci } 75162306a36Sopenharmony_ci } 75262306a36Sopenharmony_ci } 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci if (atomic_read(&a->disable_cnt) == 0) 75562306a36Sopenharmony_ci esas2r_do_deferred_processes(a); 75662306a36Sopenharmony_ci} 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci/* 75962306a36Sopenharmony_ci * Send the specified task management function to the target and LUN 76062306a36Sopenharmony_ci * specified in rqaux. in addition, immediately abort any commands that 76162306a36Sopenharmony_ci * are queued but not sent to the device according to the rules specified 76262306a36Sopenharmony_ci * by the task management function. 76362306a36Sopenharmony_ci */ 76462306a36Sopenharmony_cibool esas2r_send_task_mgmt(struct esas2r_adapter *a, 76562306a36Sopenharmony_ci struct esas2r_request *rqaux, u8 task_mgt_func) 76662306a36Sopenharmony_ci{ 76762306a36Sopenharmony_ci u16 targetid = rqaux->target_id; 76862306a36Sopenharmony_ci u8 lun = (u8)le32_to_cpu(rqaux->vrq->scsi.flags); 76962306a36Sopenharmony_ci bool ret = false; 77062306a36Sopenharmony_ci struct esas2r_request *rq; 77162306a36Sopenharmony_ci struct list_head *next, *element; 77262306a36Sopenharmony_ci unsigned long flags; 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci LIST_HEAD(comp_list); 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci esas2r_trace_enter(); 77762306a36Sopenharmony_ci esas2r_trace("rqaux:%p", rqaux); 77862306a36Sopenharmony_ci esas2r_trace("task_mgt_func:%x", task_mgt_func); 77962306a36Sopenharmony_ci spin_lock_irqsave(&a->queue_lock, flags); 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci /* search the defer queue looking for requests for the device */ 78262306a36Sopenharmony_ci list_for_each_safe(element, next, &a->defer_list) { 78362306a36Sopenharmony_ci rq = list_entry(element, struct esas2r_request, req_list); 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci if (rq->vrq->scsi.function == VDA_FUNC_SCSI 78662306a36Sopenharmony_ci && rq->target_id == targetid 78762306a36Sopenharmony_ci && (((u8)le32_to_cpu(rq->vrq->scsi.flags)) == lun 78862306a36Sopenharmony_ci || task_mgt_func == 0x20)) { /* target reset */ 78962306a36Sopenharmony_ci /* Found a request affected by the task management */ 79062306a36Sopenharmony_ci if (rq->req_stat == RS_PENDING) { 79162306a36Sopenharmony_ci /* 79262306a36Sopenharmony_ci * The request is pending or waiting. We can 79362306a36Sopenharmony_ci * safelycomplete the request now. 79462306a36Sopenharmony_ci */ 79562306a36Sopenharmony_ci if (esas2r_ioreq_aborted(a, rq, RS_ABORTED)) 79662306a36Sopenharmony_ci list_add_tail(&rq->comp_list, 79762306a36Sopenharmony_ci &comp_list); 79862306a36Sopenharmony_ci } 79962306a36Sopenharmony_ci } 80062306a36Sopenharmony_ci } 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci /* Send the task management request to the firmware */ 80362306a36Sopenharmony_ci rqaux->sense_len = 0; 80462306a36Sopenharmony_ci rqaux->vrq->scsi.length = 0; 80562306a36Sopenharmony_ci rqaux->target_id = targetid; 80662306a36Sopenharmony_ci rqaux->vrq->scsi.flags |= cpu_to_le32(lun); 80762306a36Sopenharmony_ci memset(rqaux->vrq->scsi.cdb, 0, sizeof(rqaux->vrq->scsi.cdb)); 80862306a36Sopenharmony_ci rqaux->vrq->scsi.flags |= 80962306a36Sopenharmony_ci cpu_to_le16(task_mgt_func * LOBIT(FCP_CMND_TM_MASK)); 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci if (test_bit(AF_FLASHING, &a->flags)) { 81262306a36Sopenharmony_ci /* Assume success. if there are active requests, return busy */ 81362306a36Sopenharmony_ci rqaux->req_stat = RS_SUCCESS; 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ci list_for_each_safe(element, next, &a->active_list) { 81662306a36Sopenharmony_ci rq = list_entry(element, struct esas2r_request, 81762306a36Sopenharmony_ci req_list); 81862306a36Sopenharmony_ci if (rq->vrq->scsi.function == VDA_FUNC_SCSI 81962306a36Sopenharmony_ci && rq->target_id == targetid 82062306a36Sopenharmony_ci && (((u8)le32_to_cpu(rq->vrq->scsi.flags)) == lun 82162306a36Sopenharmony_ci || task_mgt_func == 0x20)) /* target reset */ 82262306a36Sopenharmony_ci rqaux->req_stat = RS_BUSY; 82362306a36Sopenharmony_ci } 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci ret = true; 82662306a36Sopenharmony_ci } 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_ci spin_unlock_irqrestore(&a->queue_lock, flags); 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci if (!test_bit(AF_FLASHING, &a->flags)) 83162306a36Sopenharmony_ci esas2r_start_request(a, rqaux); 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci esas2r_comp_list_drain(a, &comp_list); 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci if (atomic_read(&a->disable_cnt) == 0) 83662306a36Sopenharmony_ci esas2r_do_deferred_processes(a); 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci esas2r_trace_exit(); 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci return ret; 84162306a36Sopenharmony_ci} 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_civoid esas2r_reset_bus(struct esas2r_adapter *a) 84462306a36Sopenharmony_ci{ 84562306a36Sopenharmony_ci esas2r_log(ESAS2R_LOG_INFO, "performing a bus reset"); 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci if (!test_bit(AF_DEGRADED_MODE, &a->flags) && 84862306a36Sopenharmony_ci !test_bit(AF_CHPRST_PENDING, &a->flags) && 84962306a36Sopenharmony_ci !test_bit(AF_DISC_PENDING, &a->flags)) { 85062306a36Sopenharmony_ci set_bit(AF_BUSRST_NEEDED, &a->flags); 85162306a36Sopenharmony_ci set_bit(AF_BUSRST_PENDING, &a->flags); 85262306a36Sopenharmony_ci set_bit(AF_OS_RESET, &a->flags); 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci esas2r_schedule_tasklet(a); 85562306a36Sopenharmony_ci } 85662306a36Sopenharmony_ci} 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_cibool esas2r_ioreq_aborted(struct esas2r_adapter *a, struct esas2r_request *rq, 85962306a36Sopenharmony_ci u8 status) 86062306a36Sopenharmony_ci{ 86162306a36Sopenharmony_ci esas2r_trace_enter(); 86262306a36Sopenharmony_ci esas2r_trace("rq:%p", rq); 86362306a36Sopenharmony_ci list_del_init(&rq->req_list); 86462306a36Sopenharmony_ci if (rq->timeout > RQ_MAX_TIMEOUT) { 86562306a36Sopenharmony_ci /* 86662306a36Sopenharmony_ci * The request timed out, but we could not abort it because a 86762306a36Sopenharmony_ci * chip reset occurred. Return busy status. 86862306a36Sopenharmony_ci */ 86962306a36Sopenharmony_ci rq->req_stat = RS_BUSY; 87062306a36Sopenharmony_ci esas2r_trace_exit(); 87162306a36Sopenharmony_ci return true; 87262306a36Sopenharmony_ci } 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_ci rq->req_stat = status; 87562306a36Sopenharmony_ci esas2r_trace_exit(); 87662306a36Sopenharmony_ci return true; 87762306a36Sopenharmony_ci} 878