162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * linux/drivers/scsi/esas2r/esas2r_int.c 362306a36Sopenharmony_ci * esas2r interrupt handling 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2001-2013 ATTO Technology, Inc. 662306a36Sopenharmony_ci * (mailto:linuxdrivers@attotech.com) 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ 962306a36Sopenharmony_ci/* 1062306a36Sopenharmony_ci * This program is free software; you can redistribute it and/or modify 1162306a36Sopenharmony_ci * it under the terms of the GNU General Public License as published by 1262306a36Sopenharmony_ci * the Free Software Foundation; version 2 of the License. 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * This program is distributed in the hope that it will be useful, 1562306a36Sopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of 1662306a36Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1762306a36Sopenharmony_ci * GNU General Public License for more details. 1862306a36Sopenharmony_ci * 1962306a36Sopenharmony_ci * NO WARRANTY 2062306a36Sopenharmony_ci * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR 2162306a36Sopenharmony_ci * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT 2262306a36Sopenharmony_ci * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, 2362306a36Sopenharmony_ci * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is 2462306a36Sopenharmony_ci * solely responsible for determining the appropriateness of using and 2562306a36Sopenharmony_ci * distributing the Program and assumes all risks associated with its 2662306a36Sopenharmony_ci * exercise of rights under this Agreement, including but not limited to 2762306a36Sopenharmony_ci * the risks and costs of program errors, damage to or loss of data, 2862306a36Sopenharmony_ci * programs or equipment, and unavailability or interruption of operations. 2962306a36Sopenharmony_ci * 3062306a36Sopenharmony_ci * DISCLAIMER OF LIABILITY 3162306a36Sopenharmony_ci * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY 3262306a36Sopenharmony_ci * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 3362306a36Sopenharmony_ci * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND 3462306a36Sopenharmony_ci * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 3562306a36Sopenharmony_ci * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE 3662306a36Sopenharmony_ci * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED 3762306a36Sopenharmony_ci * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES 3862306a36Sopenharmony_ci * 3962306a36Sopenharmony_ci * You should have received a copy of the GNU General Public License 4062306a36Sopenharmony_ci * along with this program; if not, write to the Free Software 4162306a36Sopenharmony_ci * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 4262306a36Sopenharmony_ci */ 4362306a36Sopenharmony_ci/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci#include "esas2r.h" 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci/* Local function prototypes */ 4862306a36Sopenharmony_cistatic void esas2r_doorbell_interrupt(struct esas2r_adapter *a, u32 doorbell); 4962306a36Sopenharmony_cistatic void esas2r_get_outbound_responses(struct esas2r_adapter *a); 5062306a36Sopenharmony_cistatic void esas2r_process_bus_reset(struct esas2r_adapter *a); 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci/* 5362306a36Sopenharmony_ci * Poll the adapter for interrupts and service them. 5462306a36Sopenharmony_ci * This function handles both legacy interrupts and MSI. 5562306a36Sopenharmony_ci */ 5662306a36Sopenharmony_civoid esas2r_polled_interrupt(struct esas2r_adapter *a) 5762306a36Sopenharmony_ci{ 5862306a36Sopenharmony_ci u32 intstat; 5962306a36Sopenharmony_ci u32 doorbell; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci esas2r_disable_chip_interrupts(a); 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci intstat = esas2r_read_register_dword(a, MU_INT_STATUS_OUT); 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci if (intstat & MU_INTSTAT_POST_OUT) { 6662306a36Sopenharmony_ci /* clear the interrupt */ 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci esas2r_write_register_dword(a, MU_OUT_LIST_INT_STAT, 6962306a36Sopenharmony_ci MU_OLIS_INT); 7062306a36Sopenharmony_ci esas2r_flush_register_dword(a, MU_OUT_LIST_INT_STAT); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci esas2r_get_outbound_responses(a); 7362306a36Sopenharmony_ci } 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci if (intstat & MU_INTSTAT_DRBL) { 7662306a36Sopenharmony_ci doorbell = esas2r_read_register_dword(a, MU_DOORBELL_OUT); 7762306a36Sopenharmony_ci if (doorbell != 0) 7862306a36Sopenharmony_ci esas2r_doorbell_interrupt(a, doorbell); 7962306a36Sopenharmony_ci } 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci esas2r_enable_chip_interrupts(a); 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci if (atomic_read(&a->disable_cnt) == 0) 8462306a36Sopenharmony_ci esas2r_do_deferred_processes(a); 8562306a36Sopenharmony_ci} 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci/* 8862306a36Sopenharmony_ci * Legacy and MSI interrupt handlers. Note that the legacy interrupt handler 8962306a36Sopenharmony_ci * schedules a TASKLET to process events, whereas the MSI handler just 9062306a36Sopenharmony_ci * processes interrupt events directly. 9162306a36Sopenharmony_ci */ 9262306a36Sopenharmony_ciirqreturn_t esas2r_interrupt(int irq, void *dev_id) 9362306a36Sopenharmony_ci{ 9462306a36Sopenharmony_ci struct esas2r_adapter *a = (struct esas2r_adapter *)dev_id; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci if (!esas2r_adapter_interrupt_pending(a)) 9762306a36Sopenharmony_ci return IRQ_NONE; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci set_bit(AF2_INT_PENDING, &a->flags2); 10062306a36Sopenharmony_ci esas2r_schedule_tasklet(a); 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci return IRQ_HANDLED; 10362306a36Sopenharmony_ci} 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_civoid esas2r_adapter_interrupt(struct esas2r_adapter *a) 10662306a36Sopenharmony_ci{ 10762306a36Sopenharmony_ci u32 doorbell; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci if (likely(a->int_stat & MU_INTSTAT_POST_OUT)) { 11062306a36Sopenharmony_ci /* clear the interrupt */ 11162306a36Sopenharmony_ci esas2r_write_register_dword(a, MU_OUT_LIST_INT_STAT, 11262306a36Sopenharmony_ci MU_OLIS_INT); 11362306a36Sopenharmony_ci esas2r_flush_register_dword(a, MU_OUT_LIST_INT_STAT); 11462306a36Sopenharmony_ci esas2r_get_outbound_responses(a); 11562306a36Sopenharmony_ci } 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci if (unlikely(a->int_stat & MU_INTSTAT_DRBL)) { 11862306a36Sopenharmony_ci doorbell = esas2r_read_register_dword(a, MU_DOORBELL_OUT); 11962306a36Sopenharmony_ci if (doorbell != 0) 12062306a36Sopenharmony_ci esas2r_doorbell_interrupt(a, doorbell); 12162306a36Sopenharmony_ci } 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci a->int_mask = ESAS2R_INT_STS_MASK; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci esas2r_enable_chip_interrupts(a); 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci if (likely(atomic_read(&a->disable_cnt) == 0)) 12862306a36Sopenharmony_ci esas2r_do_deferred_processes(a); 12962306a36Sopenharmony_ci} 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ciirqreturn_t esas2r_msi_interrupt(int irq, void *dev_id) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci struct esas2r_adapter *a = (struct esas2r_adapter *)dev_id; 13462306a36Sopenharmony_ci u32 intstat; 13562306a36Sopenharmony_ci u32 doorbell; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci intstat = esas2r_read_register_dword(a, MU_INT_STATUS_OUT); 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci if (likely(intstat & MU_INTSTAT_POST_OUT)) { 14062306a36Sopenharmony_ci /* clear the interrupt */ 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci esas2r_write_register_dword(a, MU_OUT_LIST_INT_STAT, 14362306a36Sopenharmony_ci MU_OLIS_INT); 14462306a36Sopenharmony_ci esas2r_flush_register_dword(a, MU_OUT_LIST_INT_STAT); 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci esas2r_get_outbound_responses(a); 14762306a36Sopenharmony_ci } 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci if (unlikely(intstat & MU_INTSTAT_DRBL)) { 15062306a36Sopenharmony_ci doorbell = esas2r_read_register_dword(a, MU_DOORBELL_OUT); 15162306a36Sopenharmony_ci if (doorbell != 0) 15262306a36Sopenharmony_ci esas2r_doorbell_interrupt(a, doorbell); 15362306a36Sopenharmony_ci } 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci /* 15662306a36Sopenharmony_ci * Work around a chip bug and force a new MSI to be sent if one is 15762306a36Sopenharmony_ci * still pending. 15862306a36Sopenharmony_ci */ 15962306a36Sopenharmony_ci esas2r_disable_chip_interrupts(a); 16062306a36Sopenharmony_ci esas2r_enable_chip_interrupts(a); 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci if (likely(atomic_read(&a->disable_cnt) == 0)) 16362306a36Sopenharmony_ci esas2r_do_deferred_processes(a); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci esas2r_do_tasklet_tasks(a); 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci return 1; 16862306a36Sopenharmony_ci} 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_cistatic void esas2r_handle_outbound_rsp_err(struct esas2r_adapter *a, 17362306a36Sopenharmony_ci struct esas2r_request *rq, 17462306a36Sopenharmony_ci struct atto_vda_ob_rsp *rsp) 17562306a36Sopenharmony_ci{ 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci /* 17862306a36Sopenharmony_ci * For I/O requests, only copy the response if an error 17962306a36Sopenharmony_ci * occurred and setup a callback to do error processing. 18062306a36Sopenharmony_ci */ 18162306a36Sopenharmony_ci if (unlikely(rq->req_stat != RS_SUCCESS)) { 18262306a36Sopenharmony_ci memcpy(&rq->func_rsp, &rsp->func_rsp, sizeof(rsp->func_rsp)); 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci if (rq->req_stat == RS_ABORTED) { 18562306a36Sopenharmony_ci if (rq->timeout > RQ_MAX_TIMEOUT) 18662306a36Sopenharmony_ci rq->req_stat = RS_TIMEOUT; 18762306a36Sopenharmony_ci } else if (rq->req_stat == RS_SCSI_ERROR) { 18862306a36Sopenharmony_ci u8 scsistatus = rq->func_rsp.scsi_rsp.scsi_stat; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci esas2r_trace("scsistatus: %x", scsistatus); 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci /* Any of these are a good result. */ 19362306a36Sopenharmony_ci if (scsistatus == SAM_STAT_GOOD || scsistatus == 19462306a36Sopenharmony_ci SAM_STAT_CONDITION_MET || scsistatus == 19562306a36Sopenharmony_ci SAM_STAT_INTERMEDIATE || scsistatus == 19662306a36Sopenharmony_ci SAM_STAT_INTERMEDIATE_CONDITION_MET) { 19762306a36Sopenharmony_ci rq->req_stat = RS_SUCCESS; 19862306a36Sopenharmony_ci rq->func_rsp.scsi_rsp.scsi_stat = 19962306a36Sopenharmony_ci SAM_STAT_GOOD; 20062306a36Sopenharmony_ci } 20162306a36Sopenharmony_ci } 20262306a36Sopenharmony_ci } 20362306a36Sopenharmony_ci} 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_cistatic void esas2r_get_outbound_responses(struct esas2r_adapter *a) 20662306a36Sopenharmony_ci{ 20762306a36Sopenharmony_ci struct atto_vda_ob_rsp *rsp; 20862306a36Sopenharmony_ci u32 rspput_ptr; 20962306a36Sopenharmony_ci u32 rspget_ptr; 21062306a36Sopenharmony_ci struct esas2r_request *rq; 21162306a36Sopenharmony_ci u32 handle; 21262306a36Sopenharmony_ci unsigned long flags; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci LIST_HEAD(comp_list); 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci esas2r_trace_enter(); 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci spin_lock_irqsave(&a->queue_lock, flags); 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci /* Get the outbound limit and pointers */ 22162306a36Sopenharmony_ci rspput_ptr = le32_to_cpu(*a->outbound_copy) & MU_OLC_WRT_PTR; 22262306a36Sopenharmony_ci rspget_ptr = a->last_read; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci esas2r_trace("rspput_ptr: %x, rspget_ptr: %x", rspput_ptr, rspget_ptr); 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci /* If we don't have anything to process, get out */ 22762306a36Sopenharmony_ci if (unlikely(rspget_ptr == rspput_ptr)) { 22862306a36Sopenharmony_ci spin_unlock_irqrestore(&a->queue_lock, flags); 22962306a36Sopenharmony_ci esas2r_trace_exit(); 23062306a36Sopenharmony_ci return; 23162306a36Sopenharmony_ci } 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci /* Make sure the firmware is healthy */ 23462306a36Sopenharmony_ci if (unlikely(rspput_ptr >= a->list_size)) { 23562306a36Sopenharmony_ci spin_unlock_irqrestore(&a->queue_lock, flags); 23662306a36Sopenharmony_ci esas2r_bugon(); 23762306a36Sopenharmony_ci esas2r_local_reset_adapter(a); 23862306a36Sopenharmony_ci esas2r_trace_exit(); 23962306a36Sopenharmony_ci return; 24062306a36Sopenharmony_ci } 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci do { 24362306a36Sopenharmony_ci rspget_ptr++; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci if (rspget_ptr >= a->list_size) 24662306a36Sopenharmony_ci rspget_ptr = 0; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci rsp = (struct atto_vda_ob_rsp *)a->outbound_list_md.virt_addr 24962306a36Sopenharmony_ci + rspget_ptr; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci handle = rsp->handle; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci /* Verify the handle range */ 25462306a36Sopenharmony_ci if (unlikely(LOWORD(handle) == 0 25562306a36Sopenharmony_ci || LOWORD(handle) > num_requests + 25662306a36Sopenharmony_ci num_ae_requests + 1)) { 25762306a36Sopenharmony_ci esas2r_bugon(); 25862306a36Sopenharmony_ci continue; 25962306a36Sopenharmony_ci } 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci /* Get the request for this handle */ 26262306a36Sopenharmony_ci rq = a->req_table[LOWORD(handle)]; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci if (unlikely(rq == NULL || rq->vrq->scsi.handle != handle)) { 26562306a36Sopenharmony_ci esas2r_bugon(); 26662306a36Sopenharmony_ci continue; 26762306a36Sopenharmony_ci } 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci list_del(&rq->req_list); 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci /* Get the completion status */ 27262306a36Sopenharmony_ci rq->req_stat = rsp->req_stat; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci esas2r_trace("handle: %x", handle); 27562306a36Sopenharmony_ci esas2r_trace("rq: %p", rq); 27662306a36Sopenharmony_ci esas2r_trace("req_status: %x", rq->req_stat); 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci if (likely(rq->vrq->scsi.function == VDA_FUNC_SCSI)) { 27962306a36Sopenharmony_ci esas2r_handle_outbound_rsp_err(a, rq, rsp); 28062306a36Sopenharmony_ci } else { 28162306a36Sopenharmony_ci /* 28262306a36Sopenharmony_ci * Copy the outbound completion struct for non-I/O 28362306a36Sopenharmony_ci * requests. 28462306a36Sopenharmony_ci */ 28562306a36Sopenharmony_ci memcpy(&rq->func_rsp, &rsp->func_rsp, 28662306a36Sopenharmony_ci sizeof(rsp->func_rsp)); 28762306a36Sopenharmony_ci } 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci /* Queue the request for completion. */ 29062306a36Sopenharmony_ci list_add_tail(&rq->comp_list, &comp_list); 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci } while (rspget_ptr != rspput_ptr); 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci a->last_read = rspget_ptr; 29562306a36Sopenharmony_ci spin_unlock_irqrestore(&a->queue_lock, flags); 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci esas2r_comp_list_drain(a, &comp_list); 29862306a36Sopenharmony_ci esas2r_trace_exit(); 29962306a36Sopenharmony_ci} 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci/* 30262306a36Sopenharmony_ci * Perform all deferred processes for the adapter. Deferred 30362306a36Sopenharmony_ci * processes can only be done while the current interrupt 30462306a36Sopenharmony_ci * disable_cnt for the adapter is zero. 30562306a36Sopenharmony_ci */ 30662306a36Sopenharmony_civoid esas2r_do_deferred_processes(struct esas2r_adapter *a) 30762306a36Sopenharmony_ci{ 30862306a36Sopenharmony_ci int startreqs = 2; 30962306a36Sopenharmony_ci struct esas2r_request *rq; 31062306a36Sopenharmony_ci unsigned long flags; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci /* 31362306a36Sopenharmony_ci * startreqs is used to control starting requests 31462306a36Sopenharmony_ci * that are on the deferred queue 31562306a36Sopenharmony_ci * = 0 - do not start any requests 31662306a36Sopenharmony_ci * = 1 - can start discovery requests 31762306a36Sopenharmony_ci * = 2 - can start any request 31862306a36Sopenharmony_ci */ 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci if (test_bit(AF_CHPRST_PENDING, &a->flags) || 32162306a36Sopenharmony_ci test_bit(AF_FLASHING, &a->flags)) 32262306a36Sopenharmony_ci startreqs = 0; 32362306a36Sopenharmony_ci else if (test_bit(AF_DISC_PENDING, &a->flags)) 32462306a36Sopenharmony_ci startreqs = 1; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci atomic_inc(&a->disable_cnt); 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci /* Clear off the completed list to be processed later. */ 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci if (esas2r_is_tasklet_pending(a)) { 33162306a36Sopenharmony_ci esas2r_schedule_tasklet(a); 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci startreqs = 0; 33462306a36Sopenharmony_ci } 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci /* 33762306a36Sopenharmony_ci * If we can start requests then traverse the defer queue 33862306a36Sopenharmony_ci * looking for requests to start or complete 33962306a36Sopenharmony_ci */ 34062306a36Sopenharmony_ci if (startreqs && !list_empty(&a->defer_list)) { 34162306a36Sopenharmony_ci LIST_HEAD(comp_list); 34262306a36Sopenharmony_ci struct list_head *element, *next; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci spin_lock_irqsave(&a->queue_lock, flags); 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci list_for_each_safe(element, next, &a->defer_list) { 34762306a36Sopenharmony_ci rq = list_entry(element, struct esas2r_request, 34862306a36Sopenharmony_ci req_list); 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci if (rq->req_stat != RS_PENDING) { 35162306a36Sopenharmony_ci list_del(element); 35262306a36Sopenharmony_ci list_add_tail(&rq->comp_list, &comp_list); 35362306a36Sopenharmony_ci } 35462306a36Sopenharmony_ci /* 35562306a36Sopenharmony_ci * Process discovery and OS requests separately. We 35662306a36Sopenharmony_ci * can't hold up discovery requests when discovery is 35762306a36Sopenharmony_ci * pending. In general, there may be different sets of 35862306a36Sopenharmony_ci * conditions for starting different types of requests. 35962306a36Sopenharmony_ci */ 36062306a36Sopenharmony_ci else if (rq->req_type == RT_DISC_REQ) { 36162306a36Sopenharmony_ci list_del(element); 36262306a36Sopenharmony_ci esas2r_disc_local_start_request(a, rq); 36362306a36Sopenharmony_ci } else if (startreqs == 2) { 36462306a36Sopenharmony_ci list_del(element); 36562306a36Sopenharmony_ci esas2r_local_start_request(a, rq); 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci /* 36862306a36Sopenharmony_ci * Flashing could have been set by last local 36962306a36Sopenharmony_ci * start 37062306a36Sopenharmony_ci */ 37162306a36Sopenharmony_ci if (test_bit(AF_FLASHING, &a->flags)) 37262306a36Sopenharmony_ci break; 37362306a36Sopenharmony_ci } 37462306a36Sopenharmony_ci } 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci spin_unlock_irqrestore(&a->queue_lock, flags); 37762306a36Sopenharmony_ci esas2r_comp_list_drain(a, &comp_list); 37862306a36Sopenharmony_ci } 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci atomic_dec(&a->disable_cnt); 38162306a36Sopenharmony_ci} 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci/* 38462306a36Sopenharmony_ci * Process an adapter reset (or one that is about to happen) 38562306a36Sopenharmony_ci * by making sure all outstanding requests are completed that 38662306a36Sopenharmony_ci * haven't been already. 38762306a36Sopenharmony_ci */ 38862306a36Sopenharmony_civoid esas2r_process_adapter_reset(struct esas2r_adapter *a) 38962306a36Sopenharmony_ci{ 39062306a36Sopenharmony_ci struct esas2r_request *rq = &a->general_req; 39162306a36Sopenharmony_ci unsigned long flags; 39262306a36Sopenharmony_ci struct esas2r_disc_context *dc; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci LIST_HEAD(comp_list); 39562306a36Sopenharmony_ci struct list_head *element; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci esas2r_trace_enter(); 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci spin_lock_irqsave(&a->queue_lock, flags); 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci /* abort the active discovery, if any. */ 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci if (rq->interrupt_cx) { 40462306a36Sopenharmony_ci dc = (struct esas2r_disc_context *)rq->interrupt_cx; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci dc->disc_evt = 0; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci clear_bit(AF_DISC_IN_PROG, &a->flags); 40962306a36Sopenharmony_ci } 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci /* 41262306a36Sopenharmony_ci * just clear the interrupt callback for now. it will be dequeued if 41362306a36Sopenharmony_ci * and when we find it on the active queue and we don't want the 41462306a36Sopenharmony_ci * callback called. also set the dummy completion callback in case we 41562306a36Sopenharmony_ci * were doing an I/O request. 41662306a36Sopenharmony_ci */ 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci rq->interrupt_cx = NULL; 41962306a36Sopenharmony_ci rq->interrupt_cb = NULL; 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci rq->comp_cb = esas2r_dummy_complete; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci /* Reset the read and write pointers */ 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci *a->outbound_copy = 42662306a36Sopenharmony_ci a->last_write = 42762306a36Sopenharmony_ci a->last_read = a->list_size - 1; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci set_bit(AF_COMM_LIST_TOGGLE, &a->flags); 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci /* Kill all the requests on the active list */ 43262306a36Sopenharmony_ci list_for_each(element, &a->defer_list) { 43362306a36Sopenharmony_ci rq = list_entry(element, struct esas2r_request, req_list); 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci if (rq->req_stat == RS_STARTED) 43662306a36Sopenharmony_ci if (esas2r_ioreq_aborted(a, rq, RS_ABORTED)) 43762306a36Sopenharmony_ci list_add_tail(&rq->comp_list, &comp_list); 43862306a36Sopenharmony_ci } 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci spin_unlock_irqrestore(&a->queue_lock, flags); 44162306a36Sopenharmony_ci esas2r_comp_list_drain(a, &comp_list); 44262306a36Sopenharmony_ci esas2r_process_bus_reset(a); 44362306a36Sopenharmony_ci esas2r_trace_exit(); 44462306a36Sopenharmony_ci} 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_cistatic void esas2r_process_bus_reset(struct esas2r_adapter *a) 44762306a36Sopenharmony_ci{ 44862306a36Sopenharmony_ci struct esas2r_request *rq; 44962306a36Sopenharmony_ci struct list_head *element; 45062306a36Sopenharmony_ci unsigned long flags; 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci LIST_HEAD(comp_list); 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci esas2r_trace_enter(); 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci esas2r_hdebug("reset detected"); 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci spin_lock_irqsave(&a->queue_lock, flags); 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci /* kill all the requests on the deferred queue */ 46162306a36Sopenharmony_ci list_for_each(element, &a->defer_list) { 46262306a36Sopenharmony_ci rq = list_entry(element, struct esas2r_request, req_list); 46362306a36Sopenharmony_ci if (esas2r_ioreq_aborted(a, rq, RS_ABORTED)) 46462306a36Sopenharmony_ci list_add_tail(&rq->comp_list, &comp_list); 46562306a36Sopenharmony_ci } 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci spin_unlock_irqrestore(&a->queue_lock, flags); 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci esas2r_comp_list_drain(a, &comp_list); 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci if (atomic_read(&a->disable_cnt) == 0) 47262306a36Sopenharmony_ci esas2r_do_deferred_processes(a); 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci clear_bit(AF_OS_RESET, &a->flags); 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci esas2r_trace_exit(); 47762306a36Sopenharmony_ci} 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_cistatic void esas2r_chip_rst_needed_during_tasklet(struct esas2r_adapter *a) 48062306a36Sopenharmony_ci{ 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci clear_bit(AF_CHPRST_NEEDED, &a->flags); 48362306a36Sopenharmony_ci clear_bit(AF_BUSRST_NEEDED, &a->flags); 48462306a36Sopenharmony_ci clear_bit(AF_BUSRST_DETECTED, &a->flags); 48562306a36Sopenharmony_ci clear_bit(AF_BUSRST_PENDING, &a->flags); 48662306a36Sopenharmony_ci /* 48762306a36Sopenharmony_ci * Make sure we don't get attempt more than 3 resets 48862306a36Sopenharmony_ci * when the uptime between resets does not exceed one 48962306a36Sopenharmony_ci * minute. This will stop any situation where there is 49062306a36Sopenharmony_ci * really something wrong with the hardware. The way 49162306a36Sopenharmony_ci * this works is that we start with uptime ticks at 0. 49262306a36Sopenharmony_ci * Each time we do a reset, we add 20 seconds worth to 49362306a36Sopenharmony_ci * the count. Each time a timer tick occurs, as long 49462306a36Sopenharmony_ci * as a chip reset is not pending, we decrement the 49562306a36Sopenharmony_ci * tick count. If the uptime ticks ever gets to 60 49662306a36Sopenharmony_ci * seconds worth, we disable the adapter from that 49762306a36Sopenharmony_ci * point forward. Three strikes, you're out. 49862306a36Sopenharmony_ci */ 49962306a36Sopenharmony_ci if (!esas2r_is_adapter_present(a) || (a->chip_uptime >= 50062306a36Sopenharmony_ci ESAS2R_CHP_UPTIME_MAX)) { 50162306a36Sopenharmony_ci esas2r_hdebug("*** adapter disabled ***"); 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci /* 50462306a36Sopenharmony_ci * Ok, some kind of hard failure. Make sure we 50562306a36Sopenharmony_ci * exit this loop with chip interrupts 50662306a36Sopenharmony_ci * permanently disabled so we don't lock up the 50762306a36Sopenharmony_ci * entire system. Also flag degraded mode to 50862306a36Sopenharmony_ci * prevent the heartbeat from trying to recover. 50962306a36Sopenharmony_ci */ 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci set_bit(AF_DEGRADED_MODE, &a->flags); 51262306a36Sopenharmony_ci set_bit(AF_DISABLED, &a->flags); 51362306a36Sopenharmony_ci clear_bit(AF_CHPRST_PENDING, &a->flags); 51462306a36Sopenharmony_ci clear_bit(AF_DISC_PENDING, &a->flags); 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci esas2r_disable_chip_interrupts(a); 51762306a36Sopenharmony_ci a->int_mask = 0; 51862306a36Sopenharmony_ci esas2r_process_adapter_reset(a); 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci esas2r_log(ESAS2R_LOG_CRIT, 52162306a36Sopenharmony_ci "Adapter disabled because of hardware failure"); 52262306a36Sopenharmony_ci } else { 52362306a36Sopenharmony_ci bool alrdyrst = test_and_set_bit(AF_CHPRST_STARTED, &a->flags); 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci if (!alrdyrst) 52662306a36Sopenharmony_ci /* 52762306a36Sopenharmony_ci * Only disable interrupts if this is 52862306a36Sopenharmony_ci * the first reset attempt. 52962306a36Sopenharmony_ci */ 53062306a36Sopenharmony_ci esas2r_disable_chip_interrupts(a); 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci if ((test_bit(AF_POWER_MGT, &a->flags)) && 53362306a36Sopenharmony_ci !test_bit(AF_FIRST_INIT, &a->flags) && !alrdyrst) { 53462306a36Sopenharmony_ci /* 53562306a36Sopenharmony_ci * Don't reset the chip on the first 53662306a36Sopenharmony_ci * deferred power up attempt. 53762306a36Sopenharmony_ci */ 53862306a36Sopenharmony_ci } else { 53962306a36Sopenharmony_ci esas2r_hdebug("*** resetting chip ***"); 54062306a36Sopenharmony_ci esas2r_reset_chip(a); 54162306a36Sopenharmony_ci } 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci /* Kick off the reinitialization */ 54462306a36Sopenharmony_ci a->chip_uptime += ESAS2R_CHP_UPTIME_CNT; 54562306a36Sopenharmony_ci a->chip_init_time = jiffies_to_msecs(jiffies); 54662306a36Sopenharmony_ci if (!test_bit(AF_POWER_MGT, &a->flags)) { 54762306a36Sopenharmony_ci esas2r_process_adapter_reset(a); 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci if (!alrdyrst) { 55062306a36Sopenharmony_ci /* Remove devices now that I/O is cleaned up. */ 55162306a36Sopenharmony_ci a->prev_dev_cnt = 55262306a36Sopenharmony_ci esas2r_targ_db_get_tgt_cnt(a); 55362306a36Sopenharmony_ci esas2r_targ_db_remove_all(a, false); 55462306a36Sopenharmony_ci } 55562306a36Sopenharmony_ci } 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci a->int_mask = 0; 55862306a36Sopenharmony_ci } 55962306a36Sopenharmony_ci} 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_cistatic void esas2r_handle_chip_rst_during_tasklet(struct esas2r_adapter *a) 56262306a36Sopenharmony_ci{ 56362306a36Sopenharmony_ci while (test_bit(AF_CHPRST_DETECTED, &a->flags)) { 56462306a36Sopenharmony_ci /* 56562306a36Sopenharmony_ci * Balance the enable in esas2r_initadapter_hw. 56662306a36Sopenharmony_ci * Esas2r_power_down already took care of it for power 56762306a36Sopenharmony_ci * management. 56862306a36Sopenharmony_ci */ 56962306a36Sopenharmony_ci if (!test_bit(AF_DEGRADED_MODE, &a->flags) && 57062306a36Sopenharmony_ci !test_bit(AF_POWER_MGT, &a->flags)) 57162306a36Sopenharmony_ci esas2r_disable_chip_interrupts(a); 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci /* Reinitialize the chip. */ 57462306a36Sopenharmony_ci esas2r_check_adapter(a); 57562306a36Sopenharmony_ci esas2r_init_adapter_hw(a, 0); 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci if (test_bit(AF_CHPRST_NEEDED, &a->flags)) 57862306a36Sopenharmony_ci break; 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci if (test_bit(AF_POWER_MGT, &a->flags)) { 58162306a36Sopenharmony_ci /* Recovery from power management. */ 58262306a36Sopenharmony_ci if (test_bit(AF_FIRST_INIT, &a->flags)) { 58362306a36Sopenharmony_ci /* Chip reset during normal power up */ 58462306a36Sopenharmony_ci esas2r_log(ESAS2R_LOG_CRIT, 58562306a36Sopenharmony_ci "The firmware was reset during a normal power-up sequence"); 58662306a36Sopenharmony_ci } else { 58762306a36Sopenharmony_ci /* Deferred power up complete. */ 58862306a36Sopenharmony_ci clear_bit(AF_POWER_MGT, &a->flags); 58962306a36Sopenharmony_ci esas2r_send_reset_ae(a, true); 59062306a36Sopenharmony_ci } 59162306a36Sopenharmony_ci } else { 59262306a36Sopenharmony_ci /* Recovery from online chip reset. */ 59362306a36Sopenharmony_ci if (test_bit(AF_FIRST_INIT, &a->flags)) { 59462306a36Sopenharmony_ci /* Chip reset during driver load */ 59562306a36Sopenharmony_ci } else { 59662306a36Sopenharmony_ci /* Chip reset after driver load */ 59762306a36Sopenharmony_ci esas2r_send_reset_ae(a, false); 59862306a36Sopenharmony_ci } 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci esas2r_log(ESAS2R_LOG_CRIT, 60162306a36Sopenharmony_ci "Recovering from a chip reset while the chip was online"); 60262306a36Sopenharmony_ci } 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci clear_bit(AF_CHPRST_STARTED, &a->flags); 60562306a36Sopenharmony_ci esas2r_enable_chip_interrupts(a); 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci /* 60862306a36Sopenharmony_ci * Clear this flag last! this indicates that the chip has been 60962306a36Sopenharmony_ci * reset already during initialization. 61062306a36Sopenharmony_ci */ 61162306a36Sopenharmony_ci clear_bit(AF_CHPRST_DETECTED, &a->flags); 61262306a36Sopenharmony_ci } 61362306a36Sopenharmony_ci} 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci/* Perform deferred tasks when chip interrupts are disabled */ 61762306a36Sopenharmony_civoid esas2r_do_tasklet_tasks(struct esas2r_adapter *a) 61862306a36Sopenharmony_ci{ 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci if (test_bit(AF_CHPRST_NEEDED, &a->flags) || 62162306a36Sopenharmony_ci test_bit(AF_CHPRST_DETECTED, &a->flags)) { 62262306a36Sopenharmony_ci if (test_bit(AF_CHPRST_NEEDED, &a->flags)) 62362306a36Sopenharmony_ci esas2r_chip_rst_needed_during_tasklet(a); 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci esas2r_handle_chip_rst_during_tasklet(a); 62662306a36Sopenharmony_ci } 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci if (test_bit(AF_BUSRST_NEEDED, &a->flags)) { 62962306a36Sopenharmony_ci esas2r_hdebug("hard resetting bus"); 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci clear_bit(AF_BUSRST_NEEDED, &a->flags); 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci if (test_bit(AF_FLASHING, &a->flags)) 63462306a36Sopenharmony_ci set_bit(AF_BUSRST_DETECTED, &a->flags); 63562306a36Sopenharmony_ci else 63662306a36Sopenharmony_ci esas2r_write_register_dword(a, MU_DOORBELL_IN, 63762306a36Sopenharmony_ci DRBL_RESET_BUS); 63862306a36Sopenharmony_ci } 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci if (test_bit(AF_BUSRST_DETECTED, &a->flags)) { 64162306a36Sopenharmony_ci esas2r_process_bus_reset(a); 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci esas2r_log_dev(ESAS2R_LOG_WARN, 64462306a36Sopenharmony_ci &(a->host->shost_gendev), 64562306a36Sopenharmony_ci "scsi_report_bus_reset() called"); 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci scsi_report_bus_reset(a->host, 0); 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci clear_bit(AF_BUSRST_DETECTED, &a->flags); 65062306a36Sopenharmony_ci clear_bit(AF_BUSRST_PENDING, &a->flags); 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci esas2r_log(ESAS2R_LOG_WARN, "Bus reset complete"); 65362306a36Sopenharmony_ci } 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci if (test_bit(AF_PORT_CHANGE, &a->flags)) { 65662306a36Sopenharmony_ci clear_bit(AF_PORT_CHANGE, &a->flags); 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci esas2r_targ_db_report_changes(a); 65962306a36Sopenharmony_ci } 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci if (atomic_read(&a->disable_cnt) == 0) 66262306a36Sopenharmony_ci esas2r_do_deferred_processes(a); 66362306a36Sopenharmony_ci} 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_cistatic void esas2r_doorbell_interrupt(struct esas2r_adapter *a, u32 doorbell) 66662306a36Sopenharmony_ci{ 66762306a36Sopenharmony_ci if (!(doorbell & DRBL_FORCE_INT)) { 66862306a36Sopenharmony_ci esas2r_trace_enter(); 66962306a36Sopenharmony_ci esas2r_trace("doorbell: %x", doorbell); 67062306a36Sopenharmony_ci } 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci /* First clear the doorbell bits */ 67362306a36Sopenharmony_ci esas2r_write_register_dword(a, MU_DOORBELL_OUT, doorbell); 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci if (doorbell & DRBL_RESET_BUS) 67662306a36Sopenharmony_ci set_bit(AF_BUSRST_DETECTED, &a->flags); 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci if (doorbell & DRBL_FORCE_INT) 67962306a36Sopenharmony_ci clear_bit(AF_HEARTBEAT, &a->flags); 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci if (doorbell & DRBL_PANIC_REASON_MASK) { 68262306a36Sopenharmony_ci esas2r_hdebug("*** Firmware Panic ***"); 68362306a36Sopenharmony_ci esas2r_log(ESAS2R_LOG_CRIT, "The firmware has panicked"); 68462306a36Sopenharmony_ci } 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci if (doorbell & DRBL_FW_RESET) { 68762306a36Sopenharmony_ci set_bit(AF2_COREDUMP_AVAIL, &a->flags2); 68862306a36Sopenharmony_ci esas2r_local_reset_adapter(a); 68962306a36Sopenharmony_ci } 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci if (!(doorbell & DRBL_FORCE_INT)) { 69262306a36Sopenharmony_ci esas2r_trace_exit(); 69362306a36Sopenharmony_ci } 69462306a36Sopenharmony_ci} 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_civoid esas2r_force_interrupt(struct esas2r_adapter *a) 69762306a36Sopenharmony_ci{ 69862306a36Sopenharmony_ci esas2r_write_register_dword(a, MU_DOORBELL_IN, DRBL_FORCE_INT | 69962306a36Sopenharmony_ci DRBL_DRV_VER); 70062306a36Sopenharmony_ci} 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_cistatic void esas2r_lun_event(struct esas2r_adapter *a, union atto_vda_ae *ae, 70462306a36Sopenharmony_ci u16 target, u32 length) 70562306a36Sopenharmony_ci{ 70662306a36Sopenharmony_ci struct esas2r_target *t = a->targetdb + target; 70762306a36Sopenharmony_ci u32 cplen = length; 70862306a36Sopenharmony_ci unsigned long flags; 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci if (cplen > sizeof(t->lu_event)) 71162306a36Sopenharmony_ci cplen = sizeof(t->lu_event); 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci esas2r_trace("ae->lu.dwevent: %x", ae->lu.dwevent); 71462306a36Sopenharmony_ci esas2r_trace("ae->lu.bystate: %x", ae->lu.bystate); 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci spin_lock_irqsave(&a->mem_lock, flags); 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci t->new_target_state = TS_INVALID; 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci if (ae->lu.dwevent & VDAAE_LU_LOST) { 72162306a36Sopenharmony_ci t->new_target_state = TS_NOT_PRESENT; 72262306a36Sopenharmony_ci } else { 72362306a36Sopenharmony_ci switch (ae->lu.bystate) { 72462306a36Sopenharmony_ci case VDAAE_LU_NOT_PRESENT: 72562306a36Sopenharmony_ci case VDAAE_LU_OFFLINE: 72662306a36Sopenharmony_ci case VDAAE_LU_DELETED: 72762306a36Sopenharmony_ci case VDAAE_LU_FACTORY_DISABLED: 72862306a36Sopenharmony_ci t->new_target_state = TS_NOT_PRESENT; 72962306a36Sopenharmony_ci break; 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci case VDAAE_LU_ONLINE: 73262306a36Sopenharmony_ci case VDAAE_LU_DEGRADED: 73362306a36Sopenharmony_ci t->new_target_state = TS_PRESENT; 73462306a36Sopenharmony_ci break; 73562306a36Sopenharmony_ci } 73662306a36Sopenharmony_ci } 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci if (t->new_target_state != TS_INVALID) { 73962306a36Sopenharmony_ci memcpy(&t->lu_event, &ae->lu, cplen); 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci esas2r_disc_queue_event(a, DCDE_DEV_CHANGE); 74262306a36Sopenharmony_ci } 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci spin_unlock_irqrestore(&a->mem_lock, flags); 74562306a36Sopenharmony_ci} 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_civoid esas2r_ae_complete(struct esas2r_adapter *a, struct esas2r_request *rq) 75062306a36Sopenharmony_ci{ 75162306a36Sopenharmony_ci union atto_vda_ae *ae = 75262306a36Sopenharmony_ci (union atto_vda_ae *)rq->vda_rsp_data->ae_data.event_data; 75362306a36Sopenharmony_ci u32 length = le32_to_cpu(rq->func_rsp.ae_rsp.length); 75462306a36Sopenharmony_ci union atto_vda_ae *last = 75562306a36Sopenharmony_ci (union atto_vda_ae *)(rq->vda_rsp_data->ae_data.event_data 75662306a36Sopenharmony_ci + length); 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci esas2r_trace_enter(); 75962306a36Sopenharmony_ci esas2r_trace("length: %d", length); 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci if (length > sizeof(struct atto_vda_ae_data) 76262306a36Sopenharmony_ci || (length & 3) != 0 76362306a36Sopenharmony_ci || length == 0) { 76462306a36Sopenharmony_ci esas2r_log(ESAS2R_LOG_WARN, 76562306a36Sopenharmony_ci "The AE request response length (%p) is too long: %d", 76662306a36Sopenharmony_ci rq, length); 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci esas2r_hdebug("aereq->length (0x%x) too long", length); 76962306a36Sopenharmony_ci esas2r_bugon(); 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci last = ae; 77262306a36Sopenharmony_ci } 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci while (ae < last) { 77562306a36Sopenharmony_ci u16 target; 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci esas2r_trace("ae: %p", ae); 77862306a36Sopenharmony_ci esas2r_trace("ae->hdr: %p", &(ae->hdr)); 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci length = ae->hdr.bylength; 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci if (length > (u32)((u8 *)last - (u8 *)ae) 78362306a36Sopenharmony_ci || (length & 3) != 0 78462306a36Sopenharmony_ci || length == 0) { 78562306a36Sopenharmony_ci esas2r_log(ESAS2R_LOG_CRIT, 78662306a36Sopenharmony_ci "the async event length is invalid (%p): %d", 78762306a36Sopenharmony_ci ae, length); 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci esas2r_hdebug("ae->hdr.length (0x%x) invalid", length); 79062306a36Sopenharmony_ci esas2r_bugon(); 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci break; 79362306a36Sopenharmony_ci } 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci esas2r_nuxi_ae_data(ae); 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci esas2r_queue_fw_event(a, fw_event_vda_ae, ae, 79862306a36Sopenharmony_ci sizeof(union atto_vda_ae)); 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci switch (ae->hdr.bytype) { 80162306a36Sopenharmony_ci case VDAAE_HDR_TYPE_RAID: 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_ci if (ae->raid.dwflags & (VDAAE_GROUP_STATE 80462306a36Sopenharmony_ci | VDAAE_RBLD_STATE 80562306a36Sopenharmony_ci | VDAAE_MEMBER_CHG 80662306a36Sopenharmony_ci | VDAAE_PART_CHG)) { 80762306a36Sopenharmony_ci esas2r_log(ESAS2R_LOG_INFO, 80862306a36Sopenharmony_ci "RAID event received - name:%s rebuild_state:%d group_state:%d", 80962306a36Sopenharmony_ci ae->raid.acname, 81062306a36Sopenharmony_ci ae->raid.byrebuild_state, 81162306a36Sopenharmony_ci ae->raid.bygroup_state); 81262306a36Sopenharmony_ci } 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci break; 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci case VDAAE_HDR_TYPE_LU: 81762306a36Sopenharmony_ci esas2r_log(ESAS2R_LOG_INFO, 81862306a36Sopenharmony_ci "LUN event received: event:%d target_id:%d LUN:%d state:%d", 81962306a36Sopenharmony_ci ae->lu.dwevent, 82062306a36Sopenharmony_ci ae->lu.id.tgtlun.wtarget_id, 82162306a36Sopenharmony_ci ae->lu.id.tgtlun.bylun, 82262306a36Sopenharmony_ci ae->lu.bystate); 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci target = ae->lu.id.tgtlun.wtarget_id; 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci if (target < ESAS2R_MAX_TARGETS) 82762306a36Sopenharmony_ci esas2r_lun_event(a, ae, target, length); 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci break; 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci case VDAAE_HDR_TYPE_DISK: 83262306a36Sopenharmony_ci esas2r_log(ESAS2R_LOG_INFO, "Disk event received"); 83362306a36Sopenharmony_ci break; 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci default: 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci /* Silently ignore the rest and let the apps deal with 83862306a36Sopenharmony_ci * them. 83962306a36Sopenharmony_ci */ 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci break; 84262306a36Sopenharmony_ci } 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci ae = (union atto_vda_ae *)((u8 *)ae + length); 84562306a36Sopenharmony_ci } 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci /* Now requeue it. */ 84862306a36Sopenharmony_ci esas2r_start_ae_request(a, rq); 84962306a36Sopenharmony_ci esas2r_trace_exit(); 85062306a36Sopenharmony_ci} 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci/* Send an asynchronous event for a chip reset or power management. */ 85362306a36Sopenharmony_civoid esas2r_send_reset_ae(struct esas2r_adapter *a, bool pwr_mgt) 85462306a36Sopenharmony_ci{ 85562306a36Sopenharmony_ci struct atto_vda_ae_hdr ae; 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci if (pwr_mgt) 85862306a36Sopenharmony_ci ae.bytype = VDAAE_HDR_TYPE_PWRMGT; 85962306a36Sopenharmony_ci else 86062306a36Sopenharmony_ci ae.bytype = VDAAE_HDR_TYPE_RESET; 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ci ae.byversion = VDAAE_HDR_VER_0; 86362306a36Sopenharmony_ci ae.byflags = 0; 86462306a36Sopenharmony_ci ae.bylength = (u8)sizeof(struct atto_vda_ae_hdr); 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_ci if (pwr_mgt) { 86762306a36Sopenharmony_ci esas2r_hdebug("*** sending power management AE ***"); 86862306a36Sopenharmony_ci } else { 86962306a36Sopenharmony_ci esas2r_hdebug("*** sending reset AE ***"); 87062306a36Sopenharmony_ci } 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_ci esas2r_queue_fw_event(a, fw_event_vda_ae, &ae, 87362306a36Sopenharmony_ci sizeof(union atto_vda_ae)); 87462306a36Sopenharmony_ci} 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_civoid esas2r_dummy_complete(struct esas2r_adapter *a, struct esas2r_request *rq) 87762306a36Sopenharmony_ci{} 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_cistatic void esas2r_check_req_rsp_sense(struct esas2r_adapter *a, 88062306a36Sopenharmony_ci struct esas2r_request *rq) 88162306a36Sopenharmony_ci{ 88262306a36Sopenharmony_ci u8 snslen, snslen2; 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci snslen = snslen2 = rq->func_rsp.scsi_rsp.sense_len; 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_ci if (snslen > rq->sense_len) 88762306a36Sopenharmony_ci snslen = rq->sense_len; 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci if (snslen) { 89062306a36Sopenharmony_ci if (rq->sense_buf) 89162306a36Sopenharmony_ci memcpy(rq->sense_buf, rq->data_buf, snslen); 89262306a36Sopenharmony_ci else 89362306a36Sopenharmony_ci rq->sense_buf = (u8 *)rq->data_buf; 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_ci /* See about possible sense data */ 89662306a36Sopenharmony_ci if (snslen2 > 0x0c) { 89762306a36Sopenharmony_ci u8 *s = (u8 *)rq->data_buf; 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ci esas2r_trace_enter(); 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci /* Report LUNS data has changed */ 90262306a36Sopenharmony_ci if (s[0x0c] == 0x3f && s[0x0d] == 0x0E) { 90362306a36Sopenharmony_ci esas2r_trace("rq->target_id: %d", 90462306a36Sopenharmony_ci rq->target_id); 90562306a36Sopenharmony_ci esas2r_target_state_changed(a, rq->target_id, 90662306a36Sopenharmony_ci TS_LUN_CHANGE); 90762306a36Sopenharmony_ci } 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_ci esas2r_trace("add_sense_key=%x", s[0x0c]); 91062306a36Sopenharmony_ci esas2r_trace("add_sense_qual=%x", s[0x0d]); 91162306a36Sopenharmony_ci esas2r_trace_exit(); 91262306a36Sopenharmony_ci } 91362306a36Sopenharmony_ci } 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci rq->sense_len = snslen; 91662306a36Sopenharmony_ci} 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_civoid esas2r_complete_request(struct esas2r_adapter *a, 92062306a36Sopenharmony_ci struct esas2r_request *rq) 92162306a36Sopenharmony_ci{ 92262306a36Sopenharmony_ci if (rq->vrq->scsi.function == VDA_FUNC_FLASH 92362306a36Sopenharmony_ci && rq->vrq->flash.sub_func == VDA_FLASH_COMMIT) 92462306a36Sopenharmony_ci clear_bit(AF_FLASHING, &a->flags); 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_ci /* See if we setup a callback to do special processing */ 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_ci if (rq->interrupt_cb) { 92962306a36Sopenharmony_ci (*rq->interrupt_cb)(a, rq); 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci if (rq->req_stat == RS_PENDING) { 93262306a36Sopenharmony_ci esas2r_start_request(a, rq); 93362306a36Sopenharmony_ci return; 93462306a36Sopenharmony_ci } 93562306a36Sopenharmony_ci } 93662306a36Sopenharmony_ci 93762306a36Sopenharmony_ci if (likely(rq->vrq->scsi.function == VDA_FUNC_SCSI) 93862306a36Sopenharmony_ci && unlikely(rq->req_stat != RS_SUCCESS)) { 93962306a36Sopenharmony_ci esas2r_check_req_rsp_sense(a, rq); 94062306a36Sopenharmony_ci esas2r_log_request_failure(a, rq); 94162306a36Sopenharmony_ci } 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_ci (*rq->comp_cb)(a, rq); 94462306a36Sopenharmony_ci} 945