18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci *  linux/drivers/scsi/esas2r/esas2r_int.c
38c2ecf20Sopenharmony_ci *      esas2r interrupt handling
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *  Copyright (c) 2001-2013 ATTO Technology, Inc.
68c2ecf20Sopenharmony_ci *  (mailto:linuxdrivers@attotech.com)
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
98c2ecf20Sopenharmony_ci/*
108c2ecf20Sopenharmony_ci *  This program is free software; you can redistribute it and/or modify
118c2ecf20Sopenharmony_ci *  it under the terms of the GNU General Public License as published by
128c2ecf20Sopenharmony_ci *  the Free Software Foundation; version 2 of the License.
138c2ecf20Sopenharmony_ci *
148c2ecf20Sopenharmony_ci *  This program is distributed in the hope that it will be useful,
158c2ecf20Sopenharmony_ci *  but WITHOUT ANY WARRANTY; without even the implied warranty of
168c2ecf20Sopenharmony_ci *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
178c2ecf20Sopenharmony_ci *  GNU General Public License for more details.
188c2ecf20Sopenharmony_ci *
198c2ecf20Sopenharmony_ci *  NO WARRANTY
208c2ecf20Sopenharmony_ci *  THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
218c2ecf20Sopenharmony_ci *  CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
228c2ecf20Sopenharmony_ci *  LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
238c2ecf20Sopenharmony_ci *  MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
248c2ecf20Sopenharmony_ci *  solely responsible for determining the appropriateness of using and
258c2ecf20Sopenharmony_ci *  distributing the Program and assumes all risks associated with its
268c2ecf20Sopenharmony_ci *  exercise of rights under this Agreement, including but not limited to
278c2ecf20Sopenharmony_ci *  the risks and costs of program errors, damage to or loss of data,
288c2ecf20Sopenharmony_ci *  programs or equipment, and unavailability or interruption of operations.
298c2ecf20Sopenharmony_ci *
308c2ecf20Sopenharmony_ci *  DISCLAIMER OF LIABILITY
318c2ecf20Sopenharmony_ci *  NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
328c2ecf20Sopenharmony_ci *  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
338c2ecf20Sopenharmony_ci *  DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
348c2ecf20Sopenharmony_ci *  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
358c2ecf20Sopenharmony_ci *  TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
368c2ecf20Sopenharmony_ci *  USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
378c2ecf20Sopenharmony_ci *  HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
388c2ecf20Sopenharmony_ci *
398c2ecf20Sopenharmony_ci *  You should have received a copy of the GNU General Public License
408c2ecf20Sopenharmony_ci *  along with this program; if not, write to the Free Software
418c2ecf20Sopenharmony_ci *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
428c2ecf20Sopenharmony_ci */
438c2ecf20Sopenharmony_ci/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci#include "esas2r.h"
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci/* Local function prototypes */
488c2ecf20Sopenharmony_cistatic void esas2r_doorbell_interrupt(struct esas2r_adapter *a, u32 doorbell);
498c2ecf20Sopenharmony_cistatic void esas2r_get_outbound_responses(struct esas2r_adapter *a);
508c2ecf20Sopenharmony_cistatic void esas2r_process_bus_reset(struct esas2r_adapter *a);
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci/*
538c2ecf20Sopenharmony_ci * Poll the adapter for interrupts and service them.
548c2ecf20Sopenharmony_ci * This function handles both legacy interrupts and MSI.
558c2ecf20Sopenharmony_ci */
568c2ecf20Sopenharmony_civoid esas2r_polled_interrupt(struct esas2r_adapter *a)
578c2ecf20Sopenharmony_ci{
588c2ecf20Sopenharmony_ci	u32 intstat;
598c2ecf20Sopenharmony_ci	u32 doorbell;
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci	esas2r_disable_chip_interrupts(a);
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	intstat = esas2r_read_register_dword(a, MU_INT_STATUS_OUT);
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	if (intstat & MU_INTSTAT_POST_OUT) {
668c2ecf20Sopenharmony_ci		/* clear the interrupt */
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci		esas2r_write_register_dword(a, MU_OUT_LIST_INT_STAT,
698c2ecf20Sopenharmony_ci					    MU_OLIS_INT);
708c2ecf20Sopenharmony_ci		esas2r_flush_register_dword(a, MU_OUT_LIST_INT_STAT);
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci		esas2r_get_outbound_responses(a);
738c2ecf20Sopenharmony_ci	}
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	if (intstat & MU_INTSTAT_DRBL) {
768c2ecf20Sopenharmony_ci		doorbell = esas2r_read_register_dword(a, MU_DOORBELL_OUT);
778c2ecf20Sopenharmony_ci		if (doorbell != 0)
788c2ecf20Sopenharmony_ci			esas2r_doorbell_interrupt(a, doorbell);
798c2ecf20Sopenharmony_ci	}
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	esas2r_enable_chip_interrupts(a);
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	if (atomic_read(&a->disable_cnt) == 0)
848c2ecf20Sopenharmony_ci		esas2r_do_deferred_processes(a);
858c2ecf20Sopenharmony_ci}
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci/*
888c2ecf20Sopenharmony_ci * Legacy and MSI interrupt handlers.  Note that the legacy interrupt handler
898c2ecf20Sopenharmony_ci * schedules a TASKLET to process events, whereas the MSI handler just
908c2ecf20Sopenharmony_ci * processes interrupt events directly.
918c2ecf20Sopenharmony_ci */
928c2ecf20Sopenharmony_ciirqreturn_t esas2r_interrupt(int irq, void *dev_id)
938c2ecf20Sopenharmony_ci{
948c2ecf20Sopenharmony_ci	struct esas2r_adapter *a = (struct esas2r_adapter *)dev_id;
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	if (!esas2r_adapter_interrupt_pending(a))
978c2ecf20Sopenharmony_ci		return IRQ_NONE;
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	set_bit(AF2_INT_PENDING, &a->flags2);
1008c2ecf20Sopenharmony_ci	esas2r_schedule_tasklet(a);
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
1038c2ecf20Sopenharmony_ci}
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_civoid esas2r_adapter_interrupt(struct esas2r_adapter *a)
1068c2ecf20Sopenharmony_ci{
1078c2ecf20Sopenharmony_ci	u32 doorbell;
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	if (likely(a->int_stat & MU_INTSTAT_POST_OUT)) {
1108c2ecf20Sopenharmony_ci		/* clear the interrupt */
1118c2ecf20Sopenharmony_ci		esas2r_write_register_dword(a, MU_OUT_LIST_INT_STAT,
1128c2ecf20Sopenharmony_ci					    MU_OLIS_INT);
1138c2ecf20Sopenharmony_ci		esas2r_flush_register_dword(a, MU_OUT_LIST_INT_STAT);
1148c2ecf20Sopenharmony_ci		esas2r_get_outbound_responses(a);
1158c2ecf20Sopenharmony_ci	}
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	if (unlikely(a->int_stat & MU_INTSTAT_DRBL)) {
1188c2ecf20Sopenharmony_ci		doorbell = esas2r_read_register_dword(a, MU_DOORBELL_OUT);
1198c2ecf20Sopenharmony_ci		if (doorbell != 0)
1208c2ecf20Sopenharmony_ci			esas2r_doorbell_interrupt(a, doorbell);
1218c2ecf20Sopenharmony_ci	}
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	a->int_mask = ESAS2R_INT_STS_MASK;
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	esas2r_enable_chip_interrupts(a);
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	if (likely(atomic_read(&a->disable_cnt) == 0))
1288c2ecf20Sopenharmony_ci		esas2r_do_deferred_processes(a);
1298c2ecf20Sopenharmony_ci}
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ciirqreturn_t esas2r_msi_interrupt(int irq, void *dev_id)
1328c2ecf20Sopenharmony_ci{
1338c2ecf20Sopenharmony_ci	struct esas2r_adapter *a = (struct esas2r_adapter *)dev_id;
1348c2ecf20Sopenharmony_ci	u32 intstat;
1358c2ecf20Sopenharmony_ci	u32 doorbell;
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	intstat = esas2r_read_register_dword(a, MU_INT_STATUS_OUT);
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	if (likely(intstat & MU_INTSTAT_POST_OUT)) {
1408c2ecf20Sopenharmony_ci		/* clear the interrupt */
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci		esas2r_write_register_dword(a, MU_OUT_LIST_INT_STAT,
1438c2ecf20Sopenharmony_ci					    MU_OLIS_INT);
1448c2ecf20Sopenharmony_ci		esas2r_flush_register_dword(a, MU_OUT_LIST_INT_STAT);
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci		esas2r_get_outbound_responses(a);
1478c2ecf20Sopenharmony_ci	}
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	if (unlikely(intstat & MU_INTSTAT_DRBL)) {
1508c2ecf20Sopenharmony_ci		doorbell = esas2r_read_register_dword(a, MU_DOORBELL_OUT);
1518c2ecf20Sopenharmony_ci		if (doorbell != 0)
1528c2ecf20Sopenharmony_ci			esas2r_doorbell_interrupt(a, doorbell);
1538c2ecf20Sopenharmony_ci	}
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	/*
1568c2ecf20Sopenharmony_ci	 * Work around a chip bug and force a new MSI to be sent if one is
1578c2ecf20Sopenharmony_ci	 * still pending.
1588c2ecf20Sopenharmony_ci	 */
1598c2ecf20Sopenharmony_ci	esas2r_disable_chip_interrupts(a);
1608c2ecf20Sopenharmony_ci	esas2r_enable_chip_interrupts(a);
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	if (likely(atomic_read(&a->disable_cnt) == 0))
1638c2ecf20Sopenharmony_ci		esas2r_do_deferred_processes(a);
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	esas2r_do_tasklet_tasks(a);
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	return 1;
1688c2ecf20Sopenharmony_ci}
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_cistatic void esas2r_handle_outbound_rsp_err(struct esas2r_adapter *a,
1738c2ecf20Sopenharmony_ci					   struct esas2r_request *rq,
1748c2ecf20Sopenharmony_ci					   struct atto_vda_ob_rsp *rsp)
1758c2ecf20Sopenharmony_ci{
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	/*
1788c2ecf20Sopenharmony_ci	 * For I/O requests, only copy the response if an error
1798c2ecf20Sopenharmony_ci	 * occurred and setup a callback to do error processing.
1808c2ecf20Sopenharmony_ci	 */
1818c2ecf20Sopenharmony_ci	if (unlikely(rq->req_stat != RS_SUCCESS)) {
1828c2ecf20Sopenharmony_ci		memcpy(&rq->func_rsp, &rsp->func_rsp, sizeof(rsp->func_rsp));
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci		if (rq->req_stat == RS_ABORTED) {
1858c2ecf20Sopenharmony_ci			if (rq->timeout > RQ_MAX_TIMEOUT)
1868c2ecf20Sopenharmony_ci				rq->req_stat = RS_TIMEOUT;
1878c2ecf20Sopenharmony_ci		} else if (rq->req_stat == RS_SCSI_ERROR) {
1888c2ecf20Sopenharmony_ci			u8 scsistatus = rq->func_rsp.scsi_rsp.scsi_stat;
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci			esas2r_trace("scsistatus: %x", scsistatus);
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci			/* Any of these are a good result. */
1938c2ecf20Sopenharmony_ci			if (scsistatus == SAM_STAT_GOOD || scsistatus ==
1948c2ecf20Sopenharmony_ci			    SAM_STAT_CONDITION_MET || scsistatus ==
1958c2ecf20Sopenharmony_ci			    SAM_STAT_INTERMEDIATE || scsistatus ==
1968c2ecf20Sopenharmony_ci			    SAM_STAT_INTERMEDIATE_CONDITION_MET) {
1978c2ecf20Sopenharmony_ci				rq->req_stat = RS_SUCCESS;
1988c2ecf20Sopenharmony_ci				rq->func_rsp.scsi_rsp.scsi_stat =
1998c2ecf20Sopenharmony_ci					SAM_STAT_GOOD;
2008c2ecf20Sopenharmony_ci			}
2018c2ecf20Sopenharmony_ci		}
2028c2ecf20Sopenharmony_ci	}
2038c2ecf20Sopenharmony_ci}
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_cistatic void esas2r_get_outbound_responses(struct esas2r_adapter *a)
2068c2ecf20Sopenharmony_ci{
2078c2ecf20Sopenharmony_ci	struct atto_vda_ob_rsp *rsp;
2088c2ecf20Sopenharmony_ci	u32 rspput_ptr;
2098c2ecf20Sopenharmony_ci	u32 rspget_ptr;
2108c2ecf20Sopenharmony_ci	struct esas2r_request *rq;
2118c2ecf20Sopenharmony_ci	u32 handle;
2128c2ecf20Sopenharmony_ci	unsigned long flags;
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	LIST_HEAD(comp_list);
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	esas2r_trace_enter();
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	spin_lock_irqsave(&a->queue_lock, flags);
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	/* Get the outbound limit and pointers */
2218c2ecf20Sopenharmony_ci	rspput_ptr = le32_to_cpu(*a->outbound_copy) & MU_OLC_WRT_PTR;
2228c2ecf20Sopenharmony_ci	rspget_ptr = a->last_read;
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	esas2r_trace("rspput_ptr: %x, rspget_ptr: %x", rspput_ptr, rspget_ptr);
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	/* If we don't have anything to process, get out */
2278c2ecf20Sopenharmony_ci	if (unlikely(rspget_ptr == rspput_ptr)) {
2288c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&a->queue_lock, flags);
2298c2ecf20Sopenharmony_ci		esas2r_trace_exit();
2308c2ecf20Sopenharmony_ci		return;
2318c2ecf20Sopenharmony_ci	}
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	/* Make sure the firmware is healthy */
2348c2ecf20Sopenharmony_ci	if (unlikely(rspput_ptr >= a->list_size)) {
2358c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&a->queue_lock, flags);
2368c2ecf20Sopenharmony_ci		esas2r_bugon();
2378c2ecf20Sopenharmony_ci		esas2r_local_reset_adapter(a);
2388c2ecf20Sopenharmony_ci		esas2r_trace_exit();
2398c2ecf20Sopenharmony_ci		return;
2408c2ecf20Sopenharmony_ci	}
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	do {
2438c2ecf20Sopenharmony_ci		rspget_ptr++;
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci		if (rspget_ptr >= a->list_size)
2468c2ecf20Sopenharmony_ci			rspget_ptr = 0;
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci		rsp = (struct atto_vda_ob_rsp *)a->outbound_list_md.virt_addr
2498c2ecf20Sopenharmony_ci		      + rspget_ptr;
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci		handle = rsp->handle;
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci		/* Verify the handle range */
2548c2ecf20Sopenharmony_ci		if (unlikely(LOWORD(handle) == 0
2558c2ecf20Sopenharmony_ci			     || LOWORD(handle) > num_requests +
2568c2ecf20Sopenharmony_ci			     num_ae_requests + 1)) {
2578c2ecf20Sopenharmony_ci			esas2r_bugon();
2588c2ecf20Sopenharmony_ci			continue;
2598c2ecf20Sopenharmony_ci		}
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci		/* Get the request for this handle */
2628c2ecf20Sopenharmony_ci		rq = a->req_table[LOWORD(handle)];
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci		if (unlikely(rq == NULL || rq->vrq->scsi.handle != handle)) {
2658c2ecf20Sopenharmony_ci			esas2r_bugon();
2668c2ecf20Sopenharmony_ci			continue;
2678c2ecf20Sopenharmony_ci		}
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci		list_del(&rq->req_list);
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci		/* Get the completion status */
2728c2ecf20Sopenharmony_ci		rq->req_stat = rsp->req_stat;
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci		esas2r_trace("handle: %x", handle);
2758c2ecf20Sopenharmony_ci		esas2r_trace("rq: %p", rq);
2768c2ecf20Sopenharmony_ci		esas2r_trace("req_status: %x", rq->req_stat);
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci		if (likely(rq->vrq->scsi.function == VDA_FUNC_SCSI)) {
2798c2ecf20Sopenharmony_ci			esas2r_handle_outbound_rsp_err(a, rq, rsp);
2808c2ecf20Sopenharmony_ci		} else {
2818c2ecf20Sopenharmony_ci			/*
2828c2ecf20Sopenharmony_ci			 * Copy the outbound completion struct for non-I/O
2838c2ecf20Sopenharmony_ci			 * requests.
2848c2ecf20Sopenharmony_ci			 */
2858c2ecf20Sopenharmony_ci			memcpy(&rq->func_rsp, &rsp->func_rsp,
2868c2ecf20Sopenharmony_ci			       sizeof(rsp->func_rsp));
2878c2ecf20Sopenharmony_ci		}
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci		/* Queue the request for completion. */
2908c2ecf20Sopenharmony_ci		list_add_tail(&rq->comp_list, &comp_list);
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	} while (rspget_ptr != rspput_ptr);
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	a->last_read = rspget_ptr;
2958c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&a->queue_lock, flags);
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	esas2r_comp_list_drain(a, &comp_list);
2988c2ecf20Sopenharmony_ci	esas2r_trace_exit();
2998c2ecf20Sopenharmony_ci}
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci/*
3028c2ecf20Sopenharmony_ci * Perform all deferred processes for the adapter.  Deferred
3038c2ecf20Sopenharmony_ci * processes can only be done while the current interrupt
3048c2ecf20Sopenharmony_ci * disable_cnt for the adapter is zero.
3058c2ecf20Sopenharmony_ci */
3068c2ecf20Sopenharmony_civoid esas2r_do_deferred_processes(struct esas2r_adapter *a)
3078c2ecf20Sopenharmony_ci{
3088c2ecf20Sopenharmony_ci	int startreqs = 2;
3098c2ecf20Sopenharmony_ci	struct esas2r_request *rq;
3108c2ecf20Sopenharmony_ci	unsigned long flags;
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	/*
3138c2ecf20Sopenharmony_ci	 * startreqs is used to control starting requests
3148c2ecf20Sopenharmony_ci	 * that are on the deferred queue
3158c2ecf20Sopenharmony_ci	 *  = 0 - do not start any requests
3168c2ecf20Sopenharmony_ci	 *  = 1 - can start discovery requests
3178c2ecf20Sopenharmony_ci	 *  = 2 - can start any request
3188c2ecf20Sopenharmony_ci	 */
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	if (test_bit(AF_CHPRST_PENDING, &a->flags) ||
3218c2ecf20Sopenharmony_ci	    test_bit(AF_FLASHING, &a->flags))
3228c2ecf20Sopenharmony_ci		startreqs = 0;
3238c2ecf20Sopenharmony_ci	else if (test_bit(AF_DISC_PENDING, &a->flags))
3248c2ecf20Sopenharmony_ci		startreqs = 1;
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	atomic_inc(&a->disable_cnt);
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci	/* Clear off the completed list to be processed later. */
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	if (esas2r_is_tasklet_pending(a)) {
3318c2ecf20Sopenharmony_ci		esas2r_schedule_tasklet(a);
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci		startreqs = 0;
3348c2ecf20Sopenharmony_ci	}
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	/*
3378c2ecf20Sopenharmony_ci	 * If we can start requests then traverse the defer queue
3388c2ecf20Sopenharmony_ci	 * looking for requests to start or complete
3398c2ecf20Sopenharmony_ci	 */
3408c2ecf20Sopenharmony_ci	if (startreqs && !list_empty(&a->defer_list)) {
3418c2ecf20Sopenharmony_ci		LIST_HEAD(comp_list);
3428c2ecf20Sopenharmony_ci		struct list_head *element, *next;
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci		spin_lock_irqsave(&a->queue_lock, flags);
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci		list_for_each_safe(element, next, &a->defer_list) {
3478c2ecf20Sopenharmony_ci			rq = list_entry(element, struct esas2r_request,
3488c2ecf20Sopenharmony_ci					req_list);
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci			if (rq->req_stat != RS_PENDING) {
3518c2ecf20Sopenharmony_ci				list_del(element);
3528c2ecf20Sopenharmony_ci				list_add_tail(&rq->comp_list, &comp_list);
3538c2ecf20Sopenharmony_ci			}
3548c2ecf20Sopenharmony_ci			/*
3558c2ecf20Sopenharmony_ci			 * Process discovery and OS requests separately.  We
3568c2ecf20Sopenharmony_ci			 * can't hold up discovery requests when discovery is
3578c2ecf20Sopenharmony_ci			 * pending.  In general, there may be different sets of
3588c2ecf20Sopenharmony_ci			 * conditions for starting different types of requests.
3598c2ecf20Sopenharmony_ci			 */
3608c2ecf20Sopenharmony_ci			else if (rq->req_type == RT_DISC_REQ) {
3618c2ecf20Sopenharmony_ci				list_del(element);
3628c2ecf20Sopenharmony_ci				esas2r_disc_local_start_request(a, rq);
3638c2ecf20Sopenharmony_ci			} else if (startreqs == 2) {
3648c2ecf20Sopenharmony_ci				list_del(element);
3658c2ecf20Sopenharmony_ci				esas2r_local_start_request(a, rq);
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci				/*
3688c2ecf20Sopenharmony_ci				 * Flashing could have been set by last local
3698c2ecf20Sopenharmony_ci				 * start
3708c2ecf20Sopenharmony_ci				 */
3718c2ecf20Sopenharmony_ci				if (test_bit(AF_FLASHING, &a->flags))
3728c2ecf20Sopenharmony_ci					break;
3738c2ecf20Sopenharmony_ci			}
3748c2ecf20Sopenharmony_ci		}
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&a->queue_lock, flags);
3778c2ecf20Sopenharmony_ci		esas2r_comp_list_drain(a, &comp_list);
3788c2ecf20Sopenharmony_ci	}
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	atomic_dec(&a->disable_cnt);
3818c2ecf20Sopenharmony_ci}
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci/*
3848c2ecf20Sopenharmony_ci * Process an adapter reset (or one that is about to happen)
3858c2ecf20Sopenharmony_ci * by making sure all outstanding requests are completed that
3868c2ecf20Sopenharmony_ci * haven't been already.
3878c2ecf20Sopenharmony_ci */
3888c2ecf20Sopenharmony_civoid esas2r_process_adapter_reset(struct esas2r_adapter *a)
3898c2ecf20Sopenharmony_ci{
3908c2ecf20Sopenharmony_ci	struct esas2r_request *rq = &a->general_req;
3918c2ecf20Sopenharmony_ci	unsigned long flags;
3928c2ecf20Sopenharmony_ci	struct esas2r_disc_context *dc;
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci	LIST_HEAD(comp_list);
3958c2ecf20Sopenharmony_ci	struct list_head *element;
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci	esas2r_trace_enter();
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci	spin_lock_irqsave(&a->queue_lock, flags);
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci	/* abort the active discovery, if any.   */
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci	if (rq->interrupt_cx) {
4048c2ecf20Sopenharmony_ci		dc = (struct esas2r_disc_context *)rq->interrupt_cx;
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci		dc->disc_evt = 0;
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci		clear_bit(AF_DISC_IN_PROG, &a->flags);
4098c2ecf20Sopenharmony_ci	}
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci	/*
4128c2ecf20Sopenharmony_ci	 * just clear the interrupt callback for now.  it will be dequeued if
4138c2ecf20Sopenharmony_ci	 * and when we find it on the active queue and we don't want the
4148c2ecf20Sopenharmony_ci	 * callback called.  also set the dummy completion callback in case we
4158c2ecf20Sopenharmony_ci	 * were doing an I/O request.
4168c2ecf20Sopenharmony_ci	 */
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	rq->interrupt_cx = NULL;
4198c2ecf20Sopenharmony_ci	rq->interrupt_cb = NULL;
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci	rq->comp_cb = esas2r_dummy_complete;
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci	/* Reset the read and write pointers */
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci	*a->outbound_copy =
4268c2ecf20Sopenharmony_ci		a->last_write =
4278c2ecf20Sopenharmony_ci			a->last_read = a->list_size - 1;
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci	set_bit(AF_COMM_LIST_TOGGLE, &a->flags);
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci	/* Kill all the requests on the active list */
4328c2ecf20Sopenharmony_ci	list_for_each(element, &a->defer_list) {
4338c2ecf20Sopenharmony_ci		rq = list_entry(element, struct esas2r_request, req_list);
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci		if (rq->req_stat == RS_STARTED)
4368c2ecf20Sopenharmony_ci			if (esas2r_ioreq_aborted(a, rq, RS_ABORTED))
4378c2ecf20Sopenharmony_ci				list_add_tail(&rq->comp_list, &comp_list);
4388c2ecf20Sopenharmony_ci	}
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&a->queue_lock, flags);
4418c2ecf20Sopenharmony_ci	esas2r_comp_list_drain(a, &comp_list);
4428c2ecf20Sopenharmony_ci	esas2r_process_bus_reset(a);
4438c2ecf20Sopenharmony_ci	esas2r_trace_exit();
4448c2ecf20Sopenharmony_ci}
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_cistatic void esas2r_process_bus_reset(struct esas2r_adapter *a)
4478c2ecf20Sopenharmony_ci{
4488c2ecf20Sopenharmony_ci	struct esas2r_request *rq;
4498c2ecf20Sopenharmony_ci	struct list_head *element;
4508c2ecf20Sopenharmony_ci	unsigned long flags;
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_ci	LIST_HEAD(comp_list);
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci	esas2r_trace_enter();
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci	esas2r_hdebug("reset detected");
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci	spin_lock_irqsave(&a->queue_lock, flags);
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci	/* kill all the requests on the deferred queue */
4618c2ecf20Sopenharmony_ci	list_for_each(element, &a->defer_list) {
4628c2ecf20Sopenharmony_ci		rq = list_entry(element, struct esas2r_request, req_list);
4638c2ecf20Sopenharmony_ci		if (esas2r_ioreq_aborted(a, rq, RS_ABORTED))
4648c2ecf20Sopenharmony_ci			list_add_tail(&rq->comp_list, &comp_list);
4658c2ecf20Sopenharmony_ci	}
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&a->queue_lock, flags);
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci	esas2r_comp_list_drain(a, &comp_list);
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci	if (atomic_read(&a->disable_cnt) == 0)
4728c2ecf20Sopenharmony_ci		esas2r_do_deferred_processes(a);
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci	clear_bit(AF_OS_RESET, &a->flags);
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci	esas2r_trace_exit();
4778c2ecf20Sopenharmony_ci}
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_cistatic void esas2r_chip_rst_needed_during_tasklet(struct esas2r_adapter *a)
4808c2ecf20Sopenharmony_ci{
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ci	clear_bit(AF_CHPRST_NEEDED, &a->flags);
4838c2ecf20Sopenharmony_ci	clear_bit(AF_BUSRST_NEEDED, &a->flags);
4848c2ecf20Sopenharmony_ci	clear_bit(AF_BUSRST_DETECTED, &a->flags);
4858c2ecf20Sopenharmony_ci	clear_bit(AF_BUSRST_PENDING, &a->flags);
4868c2ecf20Sopenharmony_ci	/*
4878c2ecf20Sopenharmony_ci	 * Make sure we don't get attempt more than 3 resets
4888c2ecf20Sopenharmony_ci	 * when the uptime between resets does not exceed one
4898c2ecf20Sopenharmony_ci	 * minute.  This will stop any situation where there is
4908c2ecf20Sopenharmony_ci	 * really something wrong with the hardware.  The way
4918c2ecf20Sopenharmony_ci	 * this works is that we start with uptime ticks at 0.
4928c2ecf20Sopenharmony_ci	 * Each time we do a reset, we add 20 seconds worth to
4938c2ecf20Sopenharmony_ci	 * the count.  Each time a timer tick occurs, as long
4948c2ecf20Sopenharmony_ci	 * as a chip reset is not pending, we decrement the
4958c2ecf20Sopenharmony_ci	 * tick count.  If the uptime ticks ever gets to 60
4968c2ecf20Sopenharmony_ci	 * seconds worth, we disable the adapter from that
4978c2ecf20Sopenharmony_ci	 * point forward.  Three strikes, you're out.
4988c2ecf20Sopenharmony_ci	 */
4998c2ecf20Sopenharmony_ci	if (!esas2r_is_adapter_present(a) || (a->chip_uptime >=
5008c2ecf20Sopenharmony_ci					      ESAS2R_CHP_UPTIME_MAX)) {
5018c2ecf20Sopenharmony_ci		esas2r_hdebug("*** adapter disabled ***");
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci		/*
5048c2ecf20Sopenharmony_ci		 * Ok, some kind of hard failure.  Make sure we
5058c2ecf20Sopenharmony_ci		 * exit this loop with chip interrupts
5068c2ecf20Sopenharmony_ci		 * permanently disabled so we don't lock up the
5078c2ecf20Sopenharmony_ci		 * entire system.  Also flag degraded mode to
5088c2ecf20Sopenharmony_ci		 * prevent the heartbeat from trying to recover.
5098c2ecf20Sopenharmony_ci		 */
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci		set_bit(AF_DEGRADED_MODE, &a->flags);
5128c2ecf20Sopenharmony_ci		set_bit(AF_DISABLED, &a->flags);
5138c2ecf20Sopenharmony_ci		clear_bit(AF_CHPRST_PENDING, &a->flags);
5148c2ecf20Sopenharmony_ci		clear_bit(AF_DISC_PENDING, &a->flags);
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_ci		esas2r_disable_chip_interrupts(a);
5178c2ecf20Sopenharmony_ci		a->int_mask = 0;
5188c2ecf20Sopenharmony_ci		esas2r_process_adapter_reset(a);
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_ci		esas2r_log(ESAS2R_LOG_CRIT,
5218c2ecf20Sopenharmony_ci			   "Adapter disabled because of hardware failure");
5228c2ecf20Sopenharmony_ci	} else {
5238c2ecf20Sopenharmony_ci		bool alrdyrst = test_and_set_bit(AF_CHPRST_STARTED, &a->flags);
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci		if (!alrdyrst)
5268c2ecf20Sopenharmony_ci			/*
5278c2ecf20Sopenharmony_ci			 * Only disable interrupts if this is
5288c2ecf20Sopenharmony_ci			 * the first reset attempt.
5298c2ecf20Sopenharmony_ci			 */
5308c2ecf20Sopenharmony_ci			esas2r_disable_chip_interrupts(a);
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ci		if ((test_bit(AF_POWER_MGT, &a->flags)) &&
5338c2ecf20Sopenharmony_ci		    !test_bit(AF_FIRST_INIT, &a->flags) && !alrdyrst) {
5348c2ecf20Sopenharmony_ci			/*
5358c2ecf20Sopenharmony_ci			 * Don't reset the chip on the first
5368c2ecf20Sopenharmony_ci			 * deferred power up attempt.
5378c2ecf20Sopenharmony_ci			 */
5388c2ecf20Sopenharmony_ci		} else {
5398c2ecf20Sopenharmony_ci			esas2r_hdebug("*** resetting chip ***");
5408c2ecf20Sopenharmony_ci			esas2r_reset_chip(a);
5418c2ecf20Sopenharmony_ci		}
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_ci		/* Kick off the reinitialization */
5448c2ecf20Sopenharmony_ci		a->chip_uptime += ESAS2R_CHP_UPTIME_CNT;
5458c2ecf20Sopenharmony_ci		a->chip_init_time = jiffies_to_msecs(jiffies);
5468c2ecf20Sopenharmony_ci		if (!test_bit(AF_POWER_MGT, &a->flags)) {
5478c2ecf20Sopenharmony_ci			esas2r_process_adapter_reset(a);
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_ci			if (!alrdyrst) {
5508c2ecf20Sopenharmony_ci				/* Remove devices now that I/O is cleaned up. */
5518c2ecf20Sopenharmony_ci				a->prev_dev_cnt =
5528c2ecf20Sopenharmony_ci					esas2r_targ_db_get_tgt_cnt(a);
5538c2ecf20Sopenharmony_ci				esas2r_targ_db_remove_all(a, false);
5548c2ecf20Sopenharmony_ci			}
5558c2ecf20Sopenharmony_ci		}
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_ci		a->int_mask = 0;
5588c2ecf20Sopenharmony_ci	}
5598c2ecf20Sopenharmony_ci}
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_cistatic void esas2r_handle_chip_rst_during_tasklet(struct esas2r_adapter *a)
5628c2ecf20Sopenharmony_ci{
5638c2ecf20Sopenharmony_ci	while (test_bit(AF_CHPRST_DETECTED, &a->flags)) {
5648c2ecf20Sopenharmony_ci		/*
5658c2ecf20Sopenharmony_ci		 * Balance the enable in esas2r_initadapter_hw.
5668c2ecf20Sopenharmony_ci		 * Esas2r_power_down already took care of it for power
5678c2ecf20Sopenharmony_ci		 * management.
5688c2ecf20Sopenharmony_ci		 */
5698c2ecf20Sopenharmony_ci		if (!test_bit(AF_DEGRADED_MODE, &a->flags) &&
5708c2ecf20Sopenharmony_ci		    !test_bit(AF_POWER_MGT, &a->flags))
5718c2ecf20Sopenharmony_ci			esas2r_disable_chip_interrupts(a);
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ci		/* Reinitialize the chip. */
5748c2ecf20Sopenharmony_ci		esas2r_check_adapter(a);
5758c2ecf20Sopenharmony_ci		esas2r_init_adapter_hw(a, 0);
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_ci		if (test_bit(AF_CHPRST_NEEDED, &a->flags))
5788c2ecf20Sopenharmony_ci			break;
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_ci		if (test_bit(AF_POWER_MGT, &a->flags)) {
5818c2ecf20Sopenharmony_ci			/* Recovery from power management. */
5828c2ecf20Sopenharmony_ci			if (test_bit(AF_FIRST_INIT, &a->flags)) {
5838c2ecf20Sopenharmony_ci				/* Chip reset during normal power up */
5848c2ecf20Sopenharmony_ci				esas2r_log(ESAS2R_LOG_CRIT,
5858c2ecf20Sopenharmony_ci					   "The firmware was reset during a normal power-up sequence");
5868c2ecf20Sopenharmony_ci			} else {
5878c2ecf20Sopenharmony_ci				/* Deferred power up complete. */
5888c2ecf20Sopenharmony_ci				clear_bit(AF_POWER_MGT, &a->flags);
5898c2ecf20Sopenharmony_ci				esas2r_send_reset_ae(a, true);
5908c2ecf20Sopenharmony_ci			}
5918c2ecf20Sopenharmony_ci		} else {
5928c2ecf20Sopenharmony_ci			/* Recovery from online chip reset. */
5938c2ecf20Sopenharmony_ci			if (test_bit(AF_FIRST_INIT, &a->flags)) {
5948c2ecf20Sopenharmony_ci				/* Chip reset during driver load */
5958c2ecf20Sopenharmony_ci			} else {
5968c2ecf20Sopenharmony_ci				/* Chip reset after driver load */
5978c2ecf20Sopenharmony_ci				esas2r_send_reset_ae(a, false);
5988c2ecf20Sopenharmony_ci			}
5998c2ecf20Sopenharmony_ci
6008c2ecf20Sopenharmony_ci			esas2r_log(ESAS2R_LOG_CRIT,
6018c2ecf20Sopenharmony_ci				   "Recovering from a chip reset while the chip was online");
6028c2ecf20Sopenharmony_ci		}
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_ci		clear_bit(AF_CHPRST_STARTED, &a->flags);
6058c2ecf20Sopenharmony_ci		esas2r_enable_chip_interrupts(a);
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_ci		/*
6088c2ecf20Sopenharmony_ci		 * Clear this flag last!  this indicates that the chip has been
6098c2ecf20Sopenharmony_ci		 * reset already during initialization.
6108c2ecf20Sopenharmony_ci		 */
6118c2ecf20Sopenharmony_ci		clear_bit(AF_CHPRST_DETECTED, &a->flags);
6128c2ecf20Sopenharmony_ci	}
6138c2ecf20Sopenharmony_ci}
6148c2ecf20Sopenharmony_ci
6158c2ecf20Sopenharmony_ci
6168c2ecf20Sopenharmony_ci/* Perform deferred tasks when chip interrupts are disabled */
6178c2ecf20Sopenharmony_civoid esas2r_do_tasklet_tasks(struct esas2r_adapter *a)
6188c2ecf20Sopenharmony_ci{
6198c2ecf20Sopenharmony_ci
6208c2ecf20Sopenharmony_ci	if (test_bit(AF_CHPRST_NEEDED, &a->flags) ||
6218c2ecf20Sopenharmony_ci	    test_bit(AF_CHPRST_DETECTED, &a->flags)) {
6228c2ecf20Sopenharmony_ci		if (test_bit(AF_CHPRST_NEEDED, &a->flags))
6238c2ecf20Sopenharmony_ci			esas2r_chip_rst_needed_during_tasklet(a);
6248c2ecf20Sopenharmony_ci
6258c2ecf20Sopenharmony_ci		esas2r_handle_chip_rst_during_tasklet(a);
6268c2ecf20Sopenharmony_ci	}
6278c2ecf20Sopenharmony_ci
6288c2ecf20Sopenharmony_ci	if (test_bit(AF_BUSRST_NEEDED, &a->flags)) {
6298c2ecf20Sopenharmony_ci		esas2r_hdebug("hard resetting bus");
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_ci		clear_bit(AF_BUSRST_NEEDED, &a->flags);
6328c2ecf20Sopenharmony_ci
6338c2ecf20Sopenharmony_ci		if (test_bit(AF_FLASHING, &a->flags))
6348c2ecf20Sopenharmony_ci			set_bit(AF_BUSRST_DETECTED, &a->flags);
6358c2ecf20Sopenharmony_ci		else
6368c2ecf20Sopenharmony_ci			esas2r_write_register_dword(a, MU_DOORBELL_IN,
6378c2ecf20Sopenharmony_ci						    DRBL_RESET_BUS);
6388c2ecf20Sopenharmony_ci	}
6398c2ecf20Sopenharmony_ci
6408c2ecf20Sopenharmony_ci	if (test_bit(AF_BUSRST_DETECTED, &a->flags)) {
6418c2ecf20Sopenharmony_ci		esas2r_process_bus_reset(a);
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_ci		esas2r_log_dev(ESAS2R_LOG_WARN,
6448c2ecf20Sopenharmony_ci			       &(a->host->shost_gendev),
6458c2ecf20Sopenharmony_ci			       "scsi_report_bus_reset() called");
6468c2ecf20Sopenharmony_ci
6478c2ecf20Sopenharmony_ci		scsi_report_bus_reset(a->host, 0);
6488c2ecf20Sopenharmony_ci
6498c2ecf20Sopenharmony_ci		clear_bit(AF_BUSRST_DETECTED, &a->flags);
6508c2ecf20Sopenharmony_ci		clear_bit(AF_BUSRST_PENDING, &a->flags);
6518c2ecf20Sopenharmony_ci
6528c2ecf20Sopenharmony_ci		esas2r_log(ESAS2R_LOG_WARN, "Bus reset complete");
6538c2ecf20Sopenharmony_ci	}
6548c2ecf20Sopenharmony_ci
6558c2ecf20Sopenharmony_ci	if (test_bit(AF_PORT_CHANGE, &a->flags)) {
6568c2ecf20Sopenharmony_ci		clear_bit(AF_PORT_CHANGE, &a->flags);
6578c2ecf20Sopenharmony_ci
6588c2ecf20Sopenharmony_ci		esas2r_targ_db_report_changes(a);
6598c2ecf20Sopenharmony_ci	}
6608c2ecf20Sopenharmony_ci
6618c2ecf20Sopenharmony_ci	if (atomic_read(&a->disable_cnt) == 0)
6628c2ecf20Sopenharmony_ci		esas2r_do_deferred_processes(a);
6638c2ecf20Sopenharmony_ci}
6648c2ecf20Sopenharmony_ci
6658c2ecf20Sopenharmony_cistatic void esas2r_doorbell_interrupt(struct esas2r_adapter *a, u32 doorbell)
6668c2ecf20Sopenharmony_ci{
6678c2ecf20Sopenharmony_ci	if (!(doorbell & DRBL_FORCE_INT)) {
6688c2ecf20Sopenharmony_ci		esas2r_trace_enter();
6698c2ecf20Sopenharmony_ci		esas2r_trace("doorbell: %x", doorbell);
6708c2ecf20Sopenharmony_ci	}
6718c2ecf20Sopenharmony_ci
6728c2ecf20Sopenharmony_ci	/* First clear the doorbell bits */
6738c2ecf20Sopenharmony_ci	esas2r_write_register_dword(a, MU_DOORBELL_OUT, doorbell);
6748c2ecf20Sopenharmony_ci
6758c2ecf20Sopenharmony_ci	if (doorbell & DRBL_RESET_BUS)
6768c2ecf20Sopenharmony_ci		set_bit(AF_BUSRST_DETECTED, &a->flags);
6778c2ecf20Sopenharmony_ci
6788c2ecf20Sopenharmony_ci	if (doorbell & DRBL_FORCE_INT)
6798c2ecf20Sopenharmony_ci		clear_bit(AF_HEARTBEAT, &a->flags);
6808c2ecf20Sopenharmony_ci
6818c2ecf20Sopenharmony_ci	if (doorbell & DRBL_PANIC_REASON_MASK) {
6828c2ecf20Sopenharmony_ci		esas2r_hdebug("*** Firmware Panic ***");
6838c2ecf20Sopenharmony_ci		esas2r_log(ESAS2R_LOG_CRIT, "The firmware has panicked");
6848c2ecf20Sopenharmony_ci	}
6858c2ecf20Sopenharmony_ci
6868c2ecf20Sopenharmony_ci	if (doorbell & DRBL_FW_RESET) {
6878c2ecf20Sopenharmony_ci		set_bit(AF2_COREDUMP_AVAIL, &a->flags2);
6888c2ecf20Sopenharmony_ci		esas2r_local_reset_adapter(a);
6898c2ecf20Sopenharmony_ci	}
6908c2ecf20Sopenharmony_ci
6918c2ecf20Sopenharmony_ci	if (!(doorbell & DRBL_FORCE_INT))
6928c2ecf20Sopenharmony_ci		esas2r_trace_exit();
6938c2ecf20Sopenharmony_ci}
6948c2ecf20Sopenharmony_ci
6958c2ecf20Sopenharmony_civoid esas2r_force_interrupt(struct esas2r_adapter *a)
6968c2ecf20Sopenharmony_ci{
6978c2ecf20Sopenharmony_ci	esas2r_write_register_dword(a, MU_DOORBELL_IN, DRBL_FORCE_INT |
6988c2ecf20Sopenharmony_ci				    DRBL_DRV_VER);
6998c2ecf20Sopenharmony_ci}
7008c2ecf20Sopenharmony_ci
7018c2ecf20Sopenharmony_ci
7028c2ecf20Sopenharmony_cistatic void esas2r_lun_event(struct esas2r_adapter *a, union atto_vda_ae *ae,
7038c2ecf20Sopenharmony_ci			     u16 target, u32 length)
7048c2ecf20Sopenharmony_ci{
7058c2ecf20Sopenharmony_ci	struct esas2r_target *t = a->targetdb + target;
7068c2ecf20Sopenharmony_ci	u32 cplen = length;
7078c2ecf20Sopenharmony_ci	unsigned long flags;
7088c2ecf20Sopenharmony_ci
7098c2ecf20Sopenharmony_ci	if (cplen > sizeof(t->lu_event))
7108c2ecf20Sopenharmony_ci		cplen = sizeof(t->lu_event);
7118c2ecf20Sopenharmony_ci
7128c2ecf20Sopenharmony_ci	esas2r_trace("ae->lu.dwevent: %x", ae->lu.dwevent);
7138c2ecf20Sopenharmony_ci	esas2r_trace("ae->lu.bystate: %x", ae->lu.bystate);
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_ci	spin_lock_irqsave(&a->mem_lock, flags);
7168c2ecf20Sopenharmony_ci
7178c2ecf20Sopenharmony_ci	t->new_target_state = TS_INVALID;
7188c2ecf20Sopenharmony_ci
7198c2ecf20Sopenharmony_ci	if (ae->lu.dwevent  & VDAAE_LU_LOST) {
7208c2ecf20Sopenharmony_ci		t->new_target_state = TS_NOT_PRESENT;
7218c2ecf20Sopenharmony_ci	} else {
7228c2ecf20Sopenharmony_ci		switch (ae->lu.bystate) {
7238c2ecf20Sopenharmony_ci		case VDAAE_LU_NOT_PRESENT:
7248c2ecf20Sopenharmony_ci		case VDAAE_LU_OFFLINE:
7258c2ecf20Sopenharmony_ci		case VDAAE_LU_DELETED:
7268c2ecf20Sopenharmony_ci		case VDAAE_LU_FACTORY_DISABLED:
7278c2ecf20Sopenharmony_ci			t->new_target_state = TS_NOT_PRESENT;
7288c2ecf20Sopenharmony_ci			break;
7298c2ecf20Sopenharmony_ci
7308c2ecf20Sopenharmony_ci		case VDAAE_LU_ONLINE:
7318c2ecf20Sopenharmony_ci		case VDAAE_LU_DEGRADED:
7328c2ecf20Sopenharmony_ci			t->new_target_state = TS_PRESENT;
7338c2ecf20Sopenharmony_ci			break;
7348c2ecf20Sopenharmony_ci		}
7358c2ecf20Sopenharmony_ci	}
7368c2ecf20Sopenharmony_ci
7378c2ecf20Sopenharmony_ci	if (t->new_target_state != TS_INVALID) {
7388c2ecf20Sopenharmony_ci		memcpy(&t->lu_event, &ae->lu, cplen);
7398c2ecf20Sopenharmony_ci
7408c2ecf20Sopenharmony_ci		esas2r_disc_queue_event(a, DCDE_DEV_CHANGE);
7418c2ecf20Sopenharmony_ci	}
7428c2ecf20Sopenharmony_ci
7438c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&a->mem_lock, flags);
7448c2ecf20Sopenharmony_ci}
7458c2ecf20Sopenharmony_ci
7468c2ecf20Sopenharmony_ci
7478c2ecf20Sopenharmony_ci
7488c2ecf20Sopenharmony_civoid esas2r_ae_complete(struct esas2r_adapter *a, struct esas2r_request *rq)
7498c2ecf20Sopenharmony_ci{
7508c2ecf20Sopenharmony_ci	union atto_vda_ae *ae =
7518c2ecf20Sopenharmony_ci		(union atto_vda_ae *)rq->vda_rsp_data->ae_data.event_data;
7528c2ecf20Sopenharmony_ci	u32 length = le32_to_cpu(rq->func_rsp.ae_rsp.length);
7538c2ecf20Sopenharmony_ci	union atto_vda_ae *last =
7548c2ecf20Sopenharmony_ci		(union atto_vda_ae *)(rq->vda_rsp_data->ae_data.event_data
7558c2ecf20Sopenharmony_ci				      + length);
7568c2ecf20Sopenharmony_ci
7578c2ecf20Sopenharmony_ci	esas2r_trace_enter();
7588c2ecf20Sopenharmony_ci	esas2r_trace("length: %d", length);
7598c2ecf20Sopenharmony_ci
7608c2ecf20Sopenharmony_ci	if (length > sizeof(struct atto_vda_ae_data)
7618c2ecf20Sopenharmony_ci	    || (length & 3) != 0
7628c2ecf20Sopenharmony_ci	    || length == 0) {
7638c2ecf20Sopenharmony_ci		esas2r_log(ESAS2R_LOG_WARN,
7648c2ecf20Sopenharmony_ci			   "The AE request response length (%p) is too long: %d",
7658c2ecf20Sopenharmony_ci			   rq, length);
7668c2ecf20Sopenharmony_ci
7678c2ecf20Sopenharmony_ci		esas2r_hdebug("aereq->length (0x%x) too long", length);
7688c2ecf20Sopenharmony_ci		esas2r_bugon();
7698c2ecf20Sopenharmony_ci
7708c2ecf20Sopenharmony_ci		last = ae;
7718c2ecf20Sopenharmony_ci	}
7728c2ecf20Sopenharmony_ci
7738c2ecf20Sopenharmony_ci	while (ae < last) {
7748c2ecf20Sopenharmony_ci		u16 target;
7758c2ecf20Sopenharmony_ci
7768c2ecf20Sopenharmony_ci		esas2r_trace("ae: %p", ae);
7778c2ecf20Sopenharmony_ci		esas2r_trace("ae->hdr: %p", &(ae->hdr));
7788c2ecf20Sopenharmony_ci
7798c2ecf20Sopenharmony_ci		length = ae->hdr.bylength;
7808c2ecf20Sopenharmony_ci
7818c2ecf20Sopenharmony_ci		if (length > (u32)((u8 *)last - (u8 *)ae)
7828c2ecf20Sopenharmony_ci		    || (length & 3) != 0
7838c2ecf20Sopenharmony_ci		    || length == 0) {
7848c2ecf20Sopenharmony_ci			esas2r_log(ESAS2R_LOG_CRIT,
7858c2ecf20Sopenharmony_ci				   "the async event length is invalid (%p): %d",
7868c2ecf20Sopenharmony_ci				   ae, length);
7878c2ecf20Sopenharmony_ci
7888c2ecf20Sopenharmony_ci			esas2r_hdebug("ae->hdr.length (0x%x) invalid", length);
7898c2ecf20Sopenharmony_ci			esas2r_bugon();
7908c2ecf20Sopenharmony_ci
7918c2ecf20Sopenharmony_ci			break;
7928c2ecf20Sopenharmony_ci		}
7938c2ecf20Sopenharmony_ci
7948c2ecf20Sopenharmony_ci		esas2r_nuxi_ae_data(ae);
7958c2ecf20Sopenharmony_ci
7968c2ecf20Sopenharmony_ci		esas2r_queue_fw_event(a, fw_event_vda_ae, ae,
7978c2ecf20Sopenharmony_ci				      sizeof(union atto_vda_ae));
7988c2ecf20Sopenharmony_ci
7998c2ecf20Sopenharmony_ci		switch (ae->hdr.bytype) {
8008c2ecf20Sopenharmony_ci		case VDAAE_HDR_TYPE_RAID:
8018c2ecf20Sopenharmony_ci
8028c2ecf20Sopenharmony_ci			if (ae->raid.dwflags & (VDAAE_GROUP_STATE
8038c2ecf20Sopenharmony_ci						| VDAAE_RBLD_STATE
8048c2ecf20Sopenharmony_ci						| VDAAE_MEMBER_CHG
8058c2ecf20Sopenharmony_ci						| VDAAE_PART_CHG)) {
8068c2ecf20Sopenharmony_ci				esas2r_log(ESAS2R_LOG_INFO,
8078c2ecf20Sopenharmony_ci					   "RAID event received - name:%s rebuild_state:%d group_state:%d",
8088c2ecf20Sopenharmony_ci					   ae->raid.acname,
8098c2ecf20Sopenharmony_ci					   ae->raid.byrebuild_state,
8108c2ecf20Sopenharmony_ci					   ae->raid.bygroup_state);
8118c2ecf20Sopenharmony_ci			}
8128c2ecf20Sopenharmony_ci
8138c2ecf20Sopenharmony_ci			break;
8148c2ecf20Sopenharmony_ci
8158c2ecf20Sopenharmony_ci		case VDAAE_HDR_TYPE_LU:
8168c2ecf20Sopenharmony_ci			esas2r_log(ESAS2R_LOG_INFO,
8178c2ecf20Sopenharmony_ci				   "LUN event received: event:%d target_id:%d LUN:%d state:%d",
8188c2ecf20Sopenharmony_ci				   ae->lu.dwevent,
8198c2ecf20Sopenharmony_ci				   ae->lu.id.tgtlun.wtarget_id,
8208c2ecf20Sopenharmony_ci				   ae->lu.id.tgtlun.bylun,
8218c2ecf20Sopenharmony_ci				   ae->lu.bystate);
8228c2ecf20Sopenharmony_ci
8238c2ecf20Sopenharmony_ci			target = ae->lu.id.tgtlun.wtarget_id;
8248c2ecf20Sopenharmony_ci
8258c2ecf20Sopenharmony_ci			if (target < ESAS2R_MAX_TARGETS)
8268c2ecf20Sopenharmony_ci				esas2r_lun_event(a, ae, target, length);
8278c2ecf20Sopenharmony_ci
8288c2ecf20Sopenharmony_ci			break;
8298c2ecf20Sopenharmony_ci
8308c2ecf20Sopenharmony_ci		case VDAAE_HDR_TYPE_DISK:
8318c2ecf20Sopenharmony_ci			esas2r_log(ESAS2R_LOG_INFO, "Disk event received");
8328c2ecf20Sopenharmony_ci			break;
8338c2ecf20Sopenharmony_ci
8348c2ecf20Sopenharmony_ci		default:
8358c2ecf20Sopenharmony_ci
8368c2ecf20Sopenharmony_ci			/* Silently ignore the rest and let the apps deal with
8378c2ecf20Sopenharmony_ci			 * them.
8388c2ecf20Sopenharmony_ci			 */
8398c2ecf20Sopenharmony_ci
8408c2ecf20Sopenharmony_ci			break;
8418c2ecf20Sopenharmony_ci		}
8428c2ecf20Sopenharmony_ci
8438c2ecf20Sopenharmony_ci		ae = (union atto_vda_ae *)((u8 *)ae + length);
8448c2ecf20Sopenharmony_ci	}
8458c2ecf20Sopenharmony_ci
8468c2ecf20Sopenharmony_ci	/* Now requeue it. */
8478c2ecf20Sopenharmony_ci	esas2r_start_ae_request(a, rq);
8488c2ecf20Sopenharmony_ci	esas2r_trace_exit();
8498c2ecf20Sopenharmony_ci}
8508c2ecf20Sopenharmony_ci
8518c2ecf20Sopenharmony_ci/* Send an asynchronous event for a chip reset or power management. */
8528c2ecf20Sopenharmony_civoid esas2r_send_reset_ae(struct esas2r_adapter *a, bool pwr_mgt)
8538c2ecf20Sopenharmony_ci{
8548c2ecf20Sopenharmony_ci	struct atto_vda_ae_hdr ae;
8558c2ecf20Sopenharmony_ci
8568c2ecf20Sopenharmony_ci	if (pwr_mgt)
8578c2ecf20Sopenharmony_ci		ae.bytype = VDAAE_HDR_TYPE_PWRMGT;
8588c2ecf20Sopenharmony_ci	else
8598c2ecf20Sopenharmony_ci		ae.bytype = VDAAE_HDR_TYPE_RESET;
8608c2ecf20Sopenharmony_ci
8618c2ecf20Sopenharmony_ci	ae.byversion = VDAAE_HDR_VER_0;
8628c2ecf20Sopenharmony_ci	ae.byflags = 0;
8638c2ecf20Sopenharmony_ci	ae.bylength = (u8)sizeof(struct atto_vda_ae_hdr);
8648c2ecf20Sopenharmony_ci
8658c2ecf20Sopenharmony_ci	if (pwr_mgt)
8668c2ecf20Sopenharmony_ci		esas2r_hdebug("*** sending power management AE ***");
8678c2ecf20Sopenharmony_ci	else
8688c2ecf20Sopenharmony_ci		esas2r_hdebug("*** sending reset AE ***");
8698c2ecf20Sopenharmony_ci
8708c2ecf20Sopenharmony_ci	esas2r_queue_fw_event(a, fw_event_vda_ae, &ae,
8718c2ecf20Sopenharmony_ci			      sizeof(union atto_vda_ae));
8728c2ecf20Sopenharmony_ci}
8738c2ecf20Sopenharmony_ci
8748c2ecf20Sopenharmony_civoid esas2r_dummy_complete(struct esas2r_adapter *a, struct esas2r_request *rq)
8758c2ecf20Sopenharmony_ci{}
8768c2ecf20Sopenharmony_ci
8778c2ecf20Sopenharmony_cistatic void esas2r_check_req_rsp_sense(struct esas2r_adapter *a,
8788c2ecf20Sopenharmony_ci				       struct esas2r_request *rq)
8798c2ecf20Sopenharmony_ci{
8808c2ecf20Sopenharmony_ci	u8 snslen, snslen2;
8818c2ecf20Sopenharmony_ci
8828c2ecf20Sopenharmony_ci	snslen = snslen2 = rq->func_rsp.scsi_rsp.sense_len;
8838c2ecf20Sopenharmony_ci
8848c2ecf20Sopenharmony_ci	if (snslen > rq->sense_len)
8858c2ecf20Sopenharmony_ci		snslen = rq->sense_len;
8868c2ecf20Sopenharmony_ci
8878c2ecf20Sopenharmony_ci	if (snslen) {
8888c2ecf20Sopenharmony_ci		if (rq->sense_buf)
8898c2ecf20Sopenharmony_ci			memcpy(rq->sense_buf, rq->data_buf, snslen);
8908c2ecf20Sopenharmony_ci		else
8918c2ecf20Sopenharmony_ci			rq->sense_buf = (u8 *)rq->data_buf;
8928c2ecf20Sopenharmony_ci
8938c2ecf20Sopenharmony_ci		/* See about possible sense data */
8948c2ecf20Sopenharmony_ci		if (snslen2 > 0x0c) {
8958c2ecf20Sopenharmony_ci			u8 *s = (u8 *)rq->data_buf;
8968c2ecf20Sopenharmony_ci
8978c2ecf20Sopenharmony_ci			esas2r_trace_enter();
8988c2ecf20Sopenharmony_ci
8998c2ecf20Sopenharmony_ci			/* Report LUNS data has changed */
9008c2ecf20Sopenharmony_ci			if (s[0x0c] == 0x3f && s[0x0d] == 0x0E) {
9018c2ecf20Sopenharmony_ci				esas2r_trace("rq->target_id: %d",
9028c2ecf20Sopenharmony_ci					     rq->target_id);
9038c2ecf20Sopenharmony_ci				esas2r_target_state_changed(a, rq->target_id,
9048c2ecf20Sopenharmony_ci							    TS_LUN_CHANGE);
9058c2ecf20Sopenharmony_ci			}
9068c2ecf20Sopenharmony_ci
9078c2ecf20Sopenharmony_ci			esas2r_trace("add_sense_key=%x", s[0x0c]);
9088c2ecf20Sopenharmony_ci			esas2r_trace("add_sense_qual=%x", s[0x0d]);
9098c2ecf20Sopenharmony_ci			esas2r_trace_exit();
9108c2ecf20Sopenharmony_ci		}
9118c2ecf20Sopenharmony_ci	}
9128c2ecf20Sopenharmony_ci
9138c2ecf20Sopenharmony_ci	rq->sense_len = snslen;
9148c2ecf20Sopenharmony_ci}
9158c2ecf20Sopenharmony_ci
9168c2ecf20Sopenharmony_ci
9178c2ecf20Sopenharmony_civoid esas2r_complete_request(struct esas2r_adapter *a,
9188c2ecf20Sopenharmony_ci			     struct esas2r_request *rq)
9198c2ecf20Sopenharmony_ci{
9208c2ecf20Sopenharmony_ci	if (rq->vrq->scsi.function == VDA_FUNC_FLASH
9218c2ecf20Sopenharmony_ci	    && rq->vrq->flash.sub_func == VDA_FLASH_COMMIT)
9228c2ecf20Sopenharmony_ci		clear_bit(AF_FLASHING, &a->flags);
9238c2ecf20Sopenharmony_ci
9248c2ecf20Sopenharmony_ci	/* See if we setup a callback to do special processing */
9258c2ecf20Sopenharmony_ci
9268c2ecf20Sopenharmony_ci	if (rq->interrupt_cb) {
9278c2ecf20Sopenharmony_ci		(*rq->interrupt_cb)(a, rq);
9288c2ecf20Sopenharmony_ci
9298c2ecf20Sopenharmony_ci		if (rq->req_stat == RS_PENDING) {
9308c2ecf20Sopenharmony_ci			esas2r_start_request(a, rq);
9318c2ecf20Sopenharmony_ci			return;
9328c2ecf20Sopenharmony_ci		}
9338c2ecf20Sopenharmony_ci	}
9348c2ecf20Sopenharmony_ci
9358c2ecf20Sopenharmony_ci	if (likely(rq->vrq->scsi.function == VDA_FUNC_SCSI)
9368c2ecf20Sopenharmony_ci	    && unlikely(rq->req_stat != RS_SUCCESS)) {
9378c2ecf20Sopenharmony_ci		esas2r_check_req_rsp_sense(a, rq);
9388c2ecf20Sopenharmony_ci		esas2r_log_request_failure(a, rq);
9398c2ecf20Sopenharmony_ci	}
9408c2ecf20Sopenharmony_ci
9418c2ecf20Sopenharmony_ci	(*rq->comp_cb)(a, rq);
9428c2ecf20Sopenharmony_ci}
943