126780d9eSBradley Grove /*
226780d9eSBradley Grove * linux/drivers/scsi/esas2r/esas2r_int.c
326780d9eSBradley Grove * esas2r interrupt handling
426780d9eSBradley Grove *
526780d9eSBradley Grove * Copyright (c) 2001-2013 ATTO Technology, Inc.
626780d9eSBradley Grove * (mailto:linuxdrivers@attotech.com)
726780d9eSBradley Grove */
826780d9eSBradley Grove /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
926780d9eSBradley Grove /*
1026780d9eSBradley Grove * This program is free software; you can redistribute it and/or modify
1126780d9eSBradley Grove * it under the terms of the GNU General Public License as published by
1226780d9eSBradley Grove * the Free Software Foundation; version 2 of the License.
1326780d9eSBradley Grove *
1426780d9eSBradley Grove * This program is distributed in the hope that it will be useful,
1526780d9eSBradley Grove * but WITHOUT ANY WARRANTY; without even the implied warranty of
1626780d9eSBradley Grove * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1726780d9eSBradley Grove * GNU General Public License for more details.
1826780d9eSBradley Grove *
1926780d9eSBradley Grove * NO WARRANTY
2026780d9eSBradley Grove * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
2126780d9eSBradley Grove * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
2226780d9eSBradley Grove * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
2326780d9eSBradley Grove * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
2426780d9eSBradley Grove * solely responsible for determining the appropriateness of using and
2526780d9eSBradley Grove * distributing the Program and assumes all risks associated with its
2626780d9eSBradley Grove * exercise of rights under this Agreement, including but not limited to
2726780d9eSBradley Grove * the risks and costs of program errors, damage to or loss of data,
2826780d9eSBradley Grove * programs or equipment, and unavailability or interruption of operations.
2926780d9eSBradley Grove *
3026780d9eSBradley Grove * DISCLAIMER OF LIABILITY
3126780d9eSBradley Grove * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
3226780d9eSBradley Grove * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
3326780d9eSBradley Grove * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
3426780d9eSBradley Grove * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
3526780d9eSBradley Grove * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
3626780d9eSBradley Grove * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
3726780d9eSBradley Grove * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
3826780d9eSBradley Grove *
3926780d9eSBradley Grove * You should have received a copy of the GNU General Public License
4026780d9eSBradley Grove * along with this program; if not, write to the Free Software
4126780d9eSBradley Grove * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
4226780d9eSBradley Grove */
4326780d9eSBradley Grove /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
4426780d9eSBradley Grove
4526780d9eSBradley Grove #include "esas2r.h"
4626780d9eSBradley Grove
4726780d9eSBradley Grove /* Local function prototypes */
4826780d9eSBradley Grove static void esas2r_doorbell_interrupt(struct esas2r_adapter *a, u32 doorbell);
4926780d9eSBradley Grove static void esas2r_get_outbound_responses(struct esas2r_adapter *a);
5026780d9eSBradley Grove static void esas2r_process_bus_reset(struct esas2r_adapter *a);
5126780d9eSBradley Grove
5226780d9eSBradley Grove /*
5326780d9eSBradley Grove * Poll the adapter for interrupts and service them.
5426780d9eSBradley Grove * This function handles both legacy interrupts and MSI.
5526780d9eSBradley Grove */
esas2r_polled_interrupt(struct esas2r_adapter * a)5626780d9eSBradley Grove void esas2r_polled_interrupt(struct esas2r_adapter *a)
5726780d9eSBradley Grove {
5826780d9eSBradley Grove u32 intstat;
5926780d9eSBradley Grove u32 doorbell;
6026780d9eSBradley Grove
6126780d9eSBradley Grove esas2r_disable_chip_interrupts(a);
6226780d9eSBradley Grove
6326780d9eSBradley Grove intstat = esas2r_read_register_dword(a, MU_INT_STATUS_OUT);
6426780d9eSBradley Grove
6526780d9eSBradley Grove if (intstat & MU_INTSTAT_POST_OUT) {
6626780d9eSBradley Grove /* clear the interrupt */
6726780d9eSBradley Grove
6826780d9eSBradley Grove esas2r_write_register_dword(a, MU_OUT_LIST_INT_STAT,
6926780d9eSBradley Grove MU_OLIS_INT);
7026780d9eSBradley Grove esas2r_flush_register_dword(a, MU_OUT_LIST_INT_STAT);
7126780d9eSBradley Grove
7226780d9eSBradley Grove esas2r_get_outbound_responses(a);
7326780d9eSBradley Grove }
7426780d9eSBradley Grove
7526780d9eSBradley Grove if (intstat & MU_INTSTAT_DRBL) {
7626780d9eSBradley Grove doorbell = esas2r_read_register_dword(a, MU_DOORBELL_OUT);
7726780d9eSBradley Grove if (doorbell != 0)
7826780d9eSBradley Grove esas2r_doorbell_interrupt(a, doorbell);
7926780d9eSBradley Grove }
8026780d9eSBradley Grove
8126780d9eSBradley Grove esas2r_enable_chip_interrupts(a);
8226780d9eSBradley Grove
8326780d9eSBradley Grove if (atomic_read(&a->disable_cnt) == 0)
8426780d9eSBradley Grove esas2r_do_deferred_processes(a);
8526780d9eSBradley Grove }
8626780d9eSBradley Grove
8726780d9eSBradley Grove /*
8826780d9eSBradley Grove * Legacy and MSI interrupt handlers. Note that the legacy interrupt handler
8926780d9eSBradley Grove * schedules a TASKLET to process events, whereas the MSI handler just
9026780d9eSBradley Grove * processes interrupt events directly.
9126780d9eSBradley Grove */
esas2r_interrupt(int irq,void * dev_id)9226780d9eSBradley Grove irqreturn_t esas2r_interrupt(int irq, void *dev_id)
9326780d9eSBradley Grove {
9426780d9eSBradley Grove struct esas2r_adapter *a = (struct esas2r_adapter *)dev_id;
9526780d9eSBradley Grove
9626780d9eSBradley Grove if (!esas2r_adapter_interrupt_pending(a))
9726780d9eSBradley Grove return IRQ_NONE;
9826780d9eSBradley Grove
999588d24eSBradley Grove set_bit(AF2_INT_PENDING, &a->flags2);
10026780d9eSBradley Grove esas2r_schedule_tasklet(a);
10126780d9eSBradley Grove
10226780d9eSBradley Grove return IRQ_HANDLED;
10326780d9eSBradley Grove }
10426780d9eSBradley Grove
esas2r_adapter_interrupt(struct esas2r_adapter * a)10526780d9eSBradley Grove void esas2r_adapter_interrupt(struct esas2r_adapter *a)
10626780d9eSBradley Grove {
10726780d9eSBradley Grove u32 doorbell;
10826780d9eSBradley Grove
10926780d9eSBradley Grove if (likely(a->int_stat & MU_INTSTAT_POST_OUT)) {
11026780d9eSBradley Grove /* clear the interrupt */
11126780d9eSBradley Grove esas2r_write_register_dword(a, MU_OUT_LIST_INT_STAT,
11226780d9eSBradley Grove MU_OLIS_INT);
11326780d9eSBradley Grove esas2r_flush_register_dword(a, MU_OUT_LIST_INT_STAT);
11426780d9eSBradley Grove esas2r_get_outbound_responses(a);
11526780d9eSBradley Grove }
11626780d9eSBradley Grove
11726780d9eSBradley Grove if (unlikely(a->int_stat & MU_INTSTAT_DRBL)) {
11826780d9eSBradley Grove doorbell = esas2r_read_register_dword(a, MU_DOORBELL_OUT);
11926780d9eSBradley Grove if (doorbell != 0)
12026780d9eSBradley Grove esas2r_doorbell_interrupt(a, doorbell);
12126780d9eSBradley Grove }
12226780d9eSBradley Grove
12326780d9eSBradley Grove a->int_mask = ESAS2R_INT_STS_MASK;
12426780d9eSBradley Grove
12526780d9eSBradley Grove esas2r_enable_chip_interrupts(a);
12626780d9eSBradley Grove
12726780d9eSBradley Grove if (likely(atomic_read(&a->disable_cnt) == 0))
12826780d9eSBradley Grove esas2r_do_deferred_processes(a);
12926780d9eSBradley Grove }
13026780d9eSBradley Grove
esas2r_msi_interrupt(int irq,void * dev_id)13126780d9eSBradley Grove irqreturn_t esas2r_msi_interrupt(int irq, void *dev_id)
13226780d9eSBradley Grove {
13326780d9eSBradley Grove struct esas2r_adapter *a = (struct esas2r_adapter *)dev_id;
13426780d9eSBradley Grove u32 intstat;
13526780d9eSBradley Grove u32 doorbell;
13626780d9eSBradley Grove
13726780d9eSBradley Grove intstat = esas2r_read_register_dword(a, MU_INT_STATUS_OUT);
13826780d9eSBradley Grove
13926780d9eSBradley Grove if (likely(intstat & MU_INTSTAT_POST_OUT)) {
14026780d9eSBradley Grove /* clear the interrupt */
14126780d9eSBradley Grove
14226780d9eSBradley Grove esas2r_write_register_dword(a, MU_OUT_LIST_INT_STAT,
14326780d9eSBradley Grove MU_OLIS_INT);
14426780d9eSBradley Grove esas2r_flush_register_dword(a, MU_OUT_LIST_INT_STAT);
14526780d9eSBradley Grove
14626780d9eSBradley Grove esas2r_get_outbound_responses(a);
14726780d9eSBradley Grove }
14826780d9eSBradley Grove
14926780d9eSBradley Grove if (unlikely(intstat & MU_INTSTAT_DRBL)) {
15026780d9eSBradley Grove doorbell = esas2r_read_register_dword(a, MU_DOORBELL_OUT);
15126780d9eSBradley Grove if (doorbell != 0)
15226780d9eSBradley Grove esas2r_doorbell_interrupt(a, doorbell);
15326780d9eSBradley Grove }
15426780d9eSBradley Grove
15526780d9eSBradley Grove /*
15626780d9eSBradley Grove * Work around a chip bug and force a new MSI to be sent if one is
15726780d9eSBradley Grove * still pending.
15826780d9eSBradley Grove */
15926780d9eSBradley Grove esas2r_disable_chip_interrupts(a);
16026780d9eSBradley Grove esas2r_enable_chip_interrupts(a);
16126780d9eSBradley Grove
16226780d9eSBradley Grove if (likely(atomic_read(&a->disable_cnt) == 0))
16326780d9eSBradley Grove esas2r_do_deferred_processes(a);
16426780d9eSBradley Grove
16526780d9eSBradley Grove esas2r_do_tasklet_tasks(a);
16626780d9eSBradley Grove
16726780d9eSBradley Grove return 1;
16826780d9eSBradley Grove }
16926780d9eSBradley Grove
17026780d9eSBradley Grove
17126780d9eSBradley Grove
esas2r_handle_outbound_rsp_err(struct esas2r_adapter * a,struct esas2r_request * rq,struct atto_vda_ob_rsp * rsp)17226780d9eSBradley Grove static void esas2r_handle_outbound_rsp_err(struct esas2r_adapter *a,
17326780d9eSBradley Grove struct esas2r_request *rq,
17426780d9eSBradley Grove struct atto_vda_ob_rsp *rsp)
17526780d9eSBradley Grove {
17626780d9eSBradley Grove
17726780d9eSBradley Grove /*
17826780d9eSBradley Grove * For I/O requests, only copy the response if an error
17926780d9eSBradley Grove * occurred and setup a callback to do error processing.
18026780d9eSBradley Grove */
18126780d9eSBradley Grove if (unlikely(rq->req_stat != RS_SUCCESS)) {
18226780d9eSBradley Grove memcpy(&rq->func_rsp, &rsp->func_rsp, sizeof(rsp->func_rsp));
18326780d9eSBradley Grove
18426780d9eSBradley Grove if (rq->req_stat == RS_ABORTED) {
18526780d9eSBradley Grove if (rq->timeout > RQ_MAX_TIMEOUT)
18626780d9eSBradley Grove rq->req_stat = RS_TIMEOUT;
18726780d9eSBradley Grove } else if (rq->req_stat == RS_SCSI_ERROR) {
18826780d9eSBradley Grove u8 scsistatus = rq->func_rsp.scsi_rsp.scsi_stat;
18926780d9eSBradley Grove
19026780d9eSBradley Grove esas2r_trace("scsistatus: %x", scsistatus);
19126780d9eSBradley Grove
19226780d9eSBradley Grove /* Any of these are a good result. */
19326780d9eSBradley Grove if (scsistatus == SAM_STAT_GOOD || scsistatus ==
19426780d9eSBradley Grove SAM_STAT_CONDITION_MET || scsistatus ==
19526780d9eSBradley Grove SAM_STAT_INTERMEDIATE || scsistatus ==
19626780d9eSBradley Grove SAM_STAT_INTERMEDIATE_CONDITION_MET) {
19726780d9eSBradley Grove rq->req_stat = RS_SUCCESS;
19826780d9eSBradley Grove rq->func_rsp.scsi_rsp.scsi_stat =
19926780d9eSBradley Grove SAM_STAT_GOOD;
20026780d9eSBradley Grove }
20126780d9eSBradley Grove }
20226780d9eSBradley Grove }
20326780d9eSBradley Grove }
20426780d9eSBradley Grove
esas2r_get_outbound_responses(struct esas2r_adapter * a)20526780d9eSBradley Grove static void esas2r_get_outbound_responses(struct esas2r_adapter *a)
20626780d9eSBradley Grove {
20726780d9eSBradley Grove struct atto_vda_ob_rsp *rsp;
20826780d9eSBradley Grove u32 rspput_ptr;
20926780d9eSBradley Grove u32 rspget_ptr;
21026780d9eSBradley Grove struct esas2r_request *rq;
21126780d9eSBradley Grove u32 handle;
21226780d9eSBradley Grove unsigned long flags;
21326780d9eSBradley Grove
21426780d9eSBradley Grove LIST_HEAD(comp_list);
21526780d9eSBradley Grove
21626780d9eSBradley Grove esas2r_trace_enter();
21726780d9eSBradley Grove
21826780d9eSBradley Grove spin_lock_irqsave(&a->queue_lock, flags);
21926780d9eSBradley Grove
22026780d9eSBradley Grove /* Get the outbound limit and pointers */
22126780d9eSBradley Grove rspput_ptr = le32_to_cpu(*a->outbound_copy) & MU_OLC_WRT_PTR;
22226780d9eSBradley Grove rspget_ptr = a->last_read;
22326780d9eSBradley Grove
22426780d9eSBradley Grove esas2r_trace("rspput_ptr: %x, rspget_ptr: %x", rspput_ptr, rspget_ptr);
22526780d9eSBradley Grove
22626780d9eSBradley Grove /* If we don't have anything to process, get out */
22726780d9eSBradley Grove if (unlikely(rspget_ptr == rspput_ptr)) {
22826780d9eSBradley Grove spin_unlock_irqrestore(&a->queue_lock, flags);
22926780d9eSBradley Grove esas2r_trace_exit();
23026780d9eSBradley Grove return;
23126780d9eSBradley Grove }
23226780d9eSBradley Grove
23326780d9eSBradley Grove /* Make sure the firmware is healthy */
23426780d9eSBradley Grove if (unlikely(rspput_ptr >= a->list_size)) {
23526780d9eSBradley Grove spin_unlock_irqrestore(&a->queue_lock, flags);
23626780d9eSBradley Grove esas2r_bugon();
23726780d9eSBradley Grove esas2r_local_reset_adapter(a);
23826780d9eSBradley Grove esas2r_trace_exit();
23926780d9eSBradley Grove return;
24026780d9eSBradley Grove }
24126780d9eSBradley Grove
24226780d9eSBradley Grove do {
24326780d9eSBradley Grove rspget_ptr++;
24426780d9eSBradley Grove
24526780d9eSBradley Grove if (rspget_ptr >= a->list_size)
24626780d9eSBradley Grove rspget_ptr = 0;
24726780d9eSBradley Grove
24826780d9eSBradley Grove rsp = (struct atto_vda_ob_rsp *)a->outbound_list_md.virt_addr
24926780d9eSBradley Grove + rspget_ptr;
25026780d9eSBradley Grove
25126780d9eSBradley Grove handle = rsp->handle;
25226780d9eSBradley Grove
25326780d9eSBradley Grove /* Verify the handle range */
25426780d9eSBradley Grove if (unlikely(LOWORD(handle) == 0
25526780d9eSBradley Grove || LOWORD(handle) > num_requests +
25626780d9eSBradley Grove num_ae_requests + 1)) {
25726780d9eSBradley Grove esas2r_bugon();
25826780d9eSBradley Grove continue;
25926780d9eSBradley Grove }
26026780d9eSBradley Grove
26126780d9eSBradley Grove /* Get the request for this handle */
26226780d9eSBradley Grove rq = a->req_table[LOWORD(handle)];
26326780d9eSBradley Grove
26426780d9eSBradley Grove if (unlikely(rq == NULL || rq->vrq->scsi.handle != handle)) {
26526780d9eSBradley Grove esas2r_bugon();
26626780d9eSBradley Grove continue;
26726780d9eSBradley Grove }
26826780d9eSBradley Grove
26926780d9eSBradley Grove list_del(&rq->req_list);
27026780d9eSBradley Grove
27126780d9eSBradley Grove /* Get the completion status */
27226780d9eSBradley Grove rq->req_stat = rsp->req_stat;
27326780d9eSBradley Grove
27426780d9eSBradley Grove esas2r_trace("handle: %x", handle);
27526780d9eSBradley Grove esas2r_trace("rq: %p", rq);
27626780d9eSBradley Grove esas2r_trace("req_status: %x", rq->req_stat);
27726780d9eSBradley Grove
27826780d9eSBradley Grove if (likely(rq->vrq->scsi.function == VDA_FUNC_SCSI)) {
27926780d9eSBradley Grove esas2r_handle_outbound_rsp_err(a, rq, rsp);
28026780d9eSBradley Grove } else {
28126780d9eSBradley Grove /*
28226780d9eSBradley Grove * Copy the outbound completion struct for non-I/O
28326780d9eSBradley Grove * requests.
28426780d9eSBradley Grove */
28526780d9eSBradley Grove memcpy(&rq->func_rsp, &rsp->func_rsp,
28626780d9eSBradley Grove sizeof(rsp->func_rsp));
28726780d9eSBradley Grove }
28826780d9eSBradley Grove
28926780d9eSBradley Grove /* Queue the request for completion. */
29026780d9eSBradley Grove list_add_tail(&rq->comp_list, &comp_list);
29126780d9eSBradley Grove
29226780d9eSBradley Grove } while (rspget_ptr != rspput_ptr);
29326780d9eSBradley Grove
29426780d9eSBradley Grove a->last_read = rspget_ptr;
29526780d9eSBradley Grove spin_unlock_irqrestore(&a->queue_lock, flags);
29626780d9eSBradley Grove
29726780d9eSBradley Grove esas2r_comp_list_drain(a, &comp_list);
29826780d9eSBradley Grove esas2r_trace_exit();
29926780d9eSBradley Grove }
30026780d9eSBradley Grove
30126780d9eSBradley Grove /*
30226780d9eSBradley Grove * Perform all deferred processes for the adapter. Deferred
30326780d9eSBradley Grove * processes can only be done while the current interrupt
30426780d9eSBradley Grove * disable_cnt for the adapter is zero.
30526780d9eSBradley Grove */
esas2r_do_deferred_processes(struct esas2r_adapter * a)30626780d9eSBradley Grove void esas2r_do_deferred_processes(struct esas2r_adapter *a)
30726780d9eSBradley Grove {
30826780d9eSBradley Grove int startreqs = 2;
30926780d9eSBradley Grove struct esas2r_request *rq;
31026780d9eSBradley Grove unsigned long flags;
31126780d9eSBradley Grove
31226780d9eSBradley Grove /*
31326780d9eSBradley Grove * startreqs is used to control starting requests
31426780d9eSBradley Grove * that are on the deferred queue
31526780d9eSBradley Grove * = 0 - do not start any requests
31626780d9eSBradley Grove * = 1 - can start discovery requests
31726780d9eSBradley Grove * = 2 - can start any request
31826780d9eSBradley Grove */
31926780d9eSBradley Grove
3209588d24eSBradley Grove if (test_bit(AF_CHPRST_PENDING, &a->flags) ||
3219588d24eSBradley Grove test_bit(AF_FLASHING, &a->flags))
32226780d9eSBradley Grove startreqs = 0;
3239588d24eSBradley Grove else if (test_bit(AF_DISC_PENDING, &a->flags))
32426780d9eSBradley Grove startreqs = 1;
32526780d9eSBradley Grove
32626780d9eSBradley Grove atomic_inc(&a->disable_cnt);
32726780d9eSBradley Grove
32826780d9eSBradley Grove /* Clear off the completed list to be processed later. */
32926780d9eSBradley Grove
33026780d9eSBradley Grove if (esas2r_is_tasklet_pending(a)) {
33126780d9eSBradley Grove esas2r_schedule_tasklet(a);
33226780d9eSBradley Grove
33326780d9eSBradley Grove startreqs = 0;
33426780d9eSBradley Grove }
33526780d9eSBradley Grove
33626780d9eSBradley Grove /*
33726780d9eSBradley Grove * If we can start requests then traverse the defer queue
33826780d9eSBradley Grove * looking for requests to start or complete
33926780d9eSBradley Grove */
34026780d9eSBradley Grove if (startreqs && !list_empty(&a->defer_list)) {
34126780d9eSBradley Grove LIST_HEAD(comp_list);
34226780d9eSBradley Grove struct list_head *element, *next;
34326780d9eSBradley Grove
34426780d9eSBradley Grove spin_lock_irqsave(&a->queue_lock, flags);
34526780d9eSBradley Grove
34626780d9eSBradley Grove list_for_each_safe(element, next, &a->defer_list) {
34726780d9eSBradley Grove rq = list_entry(element, struct esas2r_request,
34826780d9eSBradley Grove req_list);
34926780d9eSBradley Grove
35026780d9eSBradley Grove if (rq->req_stat != RS_PENDING) {
35126780d9eSBradley Grove list_del(element);
35226780d9eSBradley Grove list_add_tail(&rq->comp_list, &comp_list);
35326780d9eSBradley Grove }
35426780d9eSBradley Grove /*
35526780d9eSBradley Grove * Process discovery and OS requests separately. We
35626780d9eSBradley Grove * can't hold up discovery requests when discovery is
35726780d9eSBradley Grove * pending. In general, there may be different sets of
35826780d9eSBradley Grove * conditions for starting different types of requests.
35926780d9eSBradley Grove */
36026780d9eSBradley Grove else if (rq->req_type == RT_DISC_REQ) {
36126780d9eSBradley Grove list_del(element);
36226780d9eSBradley Grove esas2r_disc_local_start_request(a, rq);
36326780d9eSBradley Grove } else if (startreqs == 2) {
36426780d9eSBradley Grove list_del(element);
36526780d9eSBradley Grove esas2r_local_start_request(a, rq);
36626780d9eSBradley Grove
36726780d9eSBradley Grove /*
36826780d9eSBradley Grove * Flashing could have been set by last local
36926780d9eSBradley Grove * start
37026780d9eSBradley Grove */
3719588d24eSBradley Grove if (test_bit(AF_FLASHING, &a->flags))
37226780d9eSBradley Grove break;
37326780d9eSBradley Grove }
37426780d9eSBradley Grove }
37526780d9eSBradley Grove
37626780d9eSBradley Grove spin_unlock_irqrestore(&a->queue_lock, flags);
37726780d9eSBradley Grove esas2r_comp_list_drain(a, &comp_list);
37826780d9eSBradley Grove }
37926780d9eSBradley Grove
38026780d9eSBradley Grove atomic_dec(&a->disable_cnt);
38126780d9eSBradley Grove }
38226780d9eSBradley Grove
38326780d9eSBradley Grove /*
38426780d9eSBradley Grove * Process an adapter reset (or one that is about to happen)
38526780d9eSBradley Grove * by making sure all outstanding requests are completed that
38626780d9eSBradley Grove * haven't been already.
38726780d9eSBradley Grove */
esas2r_process_adapter_reset(struct esas2r_adapter * a)38826780d9eSBradley Grove void esas2r_process_adapter_reset(struct esas2r_adapter *a)
38926780d9eSBradley Grove {
39026780d9eSBradley Grove struct esas2r_request *rq = &a->general_req;
39126780d9eSBradley Grove unsigned long flags;
39226780d9eSBradley Grove struct esas2r_disc_context *dc;
39326780d9eSBradley Grove
39426780d9eSBradley Grove LIST_HEAD(comp_list);
39526780d9eSBradley Grove struct list_head *element;
39626780d9eSBradley Grove
39726780d9eSBradley Grove esas2r_trace_enter();
39826780d9eSBradley Grove
39926780d9eSBradley Grove spin_lock_irqsave(&a->queue_lock, flags);
40026780d9eSBradley Grove
40126780d9eSBradley Grove /* abort the active discovery, if any. */
40226780d9eSBradley Grove
40326780d9eSBradley Grove if (rq->interrupt_cx) {
40426780d9eSBradley Grove dc = (struct esas2r_disc_context *)rq->interrupt_cx;
40526780d9eSBradley Grove
40626780d9eSBradley Grove dc->disc_evt = 0;
40726780d9eSBradley Grove
4089588d24eSBradley Grove clear_bit(AF_DISC_IN_PROG, &a->flags);
40926780d9eSBradley Grove }
41026780d9eSBradley Grove
41126780d9eSBradley Grove /*
41226780d9eSBradley Grove * just clear the interrupt callback for now. it will be dequeued if
41326780d9eSBradley Grove * and when we find it on the active queue and we don't want the
41426780d9eSBradley Grove * callback called. also set the dummy completion callback in case we
41526780d9eSBradley Grove * were doing an I/O request.
41626780d9eSBradley Grove */
41726780d9eSBradley Grove
41826780d9eSBradley Grove rq->interrupt_cx = NULL;
41926780d9eSBradley Grove rq->interrupt_cb = NULL;
42026780d9eSBradley Grove
42126780d9eSBradley Grove rq->comp_cb = esas2r_dummy_complete;
42226780d9eSBradley Grove
42326780d9eSBradley Grove /* Reset the read and write pointers */
42426780d9eSBradley Grove
42526780d9eSBradley Grove *a->outbound_copy =
42626780d9eSBradley Grove a->last_write =
42726780d9eSBradley Grove a->last_read = a->list_size - 1;
42826780d9eSBradley Grove
4299588d24eSBradley Grove set_bit(AF_COMM_LIST_TOGGLE, &a->flags);
43026780d9eSBradley Grove
43126780d9eSBradley Grove /* Kill all the requests on the active list */
43226780d9eSBradley Grove list_for_each(element, &a->defer_list) {
43326780d9eSBradley Grove rq = list_entry(element, struct esas2r_request, req_list);
43426780d9eSBradley Grove
43526780d9eSBradley Grove if (rq->req_stat == RS_STARTED)
43626780d9eSBradley Grove if (esas2r_ioreq_aborted(a, rq, RS_ABORTED))
43726780d9eSBradley Grove list_add_tail(&rq->comp_list, &comp_list);
43826780d9eSBradley Grove }
43926780d9eSBradley Grove
44026780d9eSBradley Grove spin_unlock_irqrestore(&a->queue_lock, flags);
44126780d9eSBradley Grove esas2r_comp_list_drain(a, &comp_list);
44226780d9eSBradley Grove esas2r_process_bus_reset(a);
44326780d9eSBradley Grove esas2r_trace_exit();
44426780d9eSBradley Grove }
44526780d9eSBradley Grove
esas2r_process_bus_reset(struct esas2r_adapter * a)44626780d9eSBradley Grove static void esas2r_process_bus_reset(struct esas2r_adapter *a)
44726780d9eSBradley Grove {
44826780d9eSBradley Grove struct esas2r_request *rq;
44926780d9eSBradley Grove struct list_head *element;
45026780d9eSBradley Grove unsigned long flags;
45126780d9eSBradley Grove
45226780d9eSBradley Grove LIST_HEAD(comp_list);
45326780d9eSBradley Grove
45426780d9eSBradley Grove esas2r_trace_enter();
45526780d9eSBradley Grove
45626780d9eSBradley Grove esas2r_hdebug("reset detected");
45726780d9eSBradley Grove
45826780d9eSBradley Grove spin_lock_irqsave(&a->queue_lock, flags);
45926780d9eSBradley Grove
46026780d9eSBradley Grove /* kill all the requests on the deferred queue */
46126780d9eSBradley Grove list_for_each(element, &a->defer_list) {
46226780d9eSBradley Grove rq = list_entry(element, struct esas2r_request, req_list);
46326780d9eSBradley Grove if (esas2r_ioreq_aborted(a, rq, RS_ABORTED))
46426780d9eSBradley Grove list_add_tail(&rq->comp_list, &comp_list);
46526780d9eSBradley Grove }
46626780d9eSBradley Grove
46726780d9eSBradley Grove spin_unlock_irqrestore(&a->queue_lock, flags);
46826780d9eSBradley Grove
46926780d9eSBradley Grove esas2r_comp_list_drain(a, &comp_list);
47026780d9eSBradley Grove
47126780d9eSBradley Grove if (atomic_read(&a->disable_cnt) == 0)
47226780d9eSBradley Grove esas2r_do_deferred_processes(a);
47326780d9eSBradley Grove
4749588d24eSBradley Grove clear_bit(AF_OS_RESET, &a->flags);
47526780d9eSBradley Grove
47626780d9eSBradley Grove esas2r_trace_exit();
47726780d9eSBradley Grove }
47826780d9eSBradley Grove
esas2r_chip_rst_needed_during_tasklet(struct esas2r_adapter * a)47926780d9eSBradley Grove static void esas2r_chip_rst_needed_during_tasklet(struct esas2r_adapter *a)
48026780d9eSBradley Grove {
48126780d9eSBradley Grove
4829588d24eSBradley Grove clear_bit(AF_CHPRST_NEEDED, &a->flags);
4839588d24eSBradley Grove clear_bit(AF_BUSRST_NEEDED, &a->flags);
4849588d24eSBradley Grove clear_bit(AF_BUSRST_DETECTED, &a->flags);
4859588d24eSBradley Grove clear_bit(AF_BUSRST_PENDING, &a->flags);
48626780d9eSBradley Grove /*
48726780d9eSBradley Grove * Make sure we don't get attempt more than 3 resets
48826780d9eSBradley Grove * when the uptime between resets does not exceed one
48926780d9eSBradley Grove * minute. This will stop any situation where there is
49026780d9eSBradley Grove * really something wrong with the hardware. The way
49126780d9eSBradley Grove * this works is that we start with uptime ticks at 0.
49226780d9eSBradley Grove * Each time we do a reset, we add 20 seconds worth to
49326780d9eSBradley Grove * the count. Each time a timer tick occurs, as long
49426780d9eSBradley Grove * as a chip reset is not pending, we decrement the
49526780d9eSBradley Grove * tick count. If the uptime ticks ever gets to 60
49626780d9eSBradley Grove * seconds worth, we disable the adapter from that
49726780d9eSBradley Grove * point forward. Three strikes, you're out.
49826780d9eSBradley Grove */
49926780d9eSBradley Grove if (!esas2r_is_adapter_present(a) || (a->chip_uptime >=
50026780d9eSBradley Grove ESAS2R_CHP_UPTIME_MAX)) {
50126780d9eSBradley Grove esas2r_hdebug("*** adapter disabled ***");
50226780d9eSBradley Grove
50326780d9eSBradley Grove /*
50426780d9eSBradley Grove * Ok, some kind of hard failure. Make sure we
50526780d9eSBradley Grove * exit this loop with chip interrupts
50626780d9eSBradley Grove * permanently disabled so we don't lock up the
50726780d9eSBradley Grove * entire system. Also flag degraded mode to
50826780d9eSBradley Grove * prevent the heartbeat from trying to recover.
50926780d9eSBradley Grove */
51026780d9eSBradley Grove
5119588d24eSBradley Grove set_bit(AF_DEGRADED_MODE, &a->flags);
5129588d24eSBradley Grove set_bit(AF_DISABLED, &a->flags);
5139588d24eSBradley Grove clear_bit(AF_CHPRST_PENDING, &a->flags);
5149588d24eSBradley Grove clear_bit(AF_DISC_PENDING, &a->flags);
51526780d9eSBradley Grove
51626780d9eSBradley Grove esas2r_disable_chip_interrupts(a);
51726780d9eSBradley Grove a->int_mask = 0;
51826780d9eSBradley Grove esas2r_process_adapter_reset(a);
51926780d9eSBradley Grove
52026780d9eSBradley Grove esas2r_log(ESAS2R_LOG_CRIT,
52126780d9eSBradley Grove "Adapter disabled because of hardware failure");
52226780d9eSBradley Grove } else {
5239588d24eSBradley Grove bool alrdyrst = test_and_set_bit(AF_CHPRST_STARTED, &a->flags);
52426780d9eSBradley Grove
5259588d24eSBradley Grove if (!alrdyrst)
52626780d9eSBradley Grove /*
52726780d9eSBradley Grove * Only disable interrupts if this is
52826780d9eSBradley Grove * the first reset attempt.
52926780d9eSBradley Grove */
53026780d9eSBradley Grove esas2r_disable_chip_interrupts(a);
53126780d9eSBradley Grove
5329588d24eSBradley Grove if ((test_bit(AF_POWER_MGT, &a->flags)) &&
5339588d24eSBradley Grove !test_bit(AF_FIRST_INIT, &a->flags) && !alrdyrst) {
53426780d9eSBradley Grove /*
53526780d9eSBradley Grove * Don't reset the chip on the first
53626780d9eSBradley Grove * deferred power up attempt.
53726780d9eSBradley Grove */
53826780d9eSBradley Grove } else {
53926780d9eSBradley Grove esas2r_hdebug("*** resetting chip ***");
54026780d9eSBradley Grove esas2r_reset_chip(a);
54126780d9eSBradley Grove }
54226780d9eSBradley Grove
54326780d9eSBradley Grove /* Kick off the reinitialization */
54426780d9eSBradley Grove a->chip_uptime += ESAS2R_CHP_UPTIME_CNT;
54526780d9eSBradley Grove a->chip_init_time = jiffies_to_msecs(jiffies);
5469588d24eSBradley Grove if (!test_bit(AF_POWER_MGT, &a->flags)) {
54726780d9eSBradley Grove esas2r_process_adapter_reset(a);
54826780d9eSBradley Grove
5499588d24eSBradley Grove if (!alrdyrst) {
55026780d9eSBradley Grove /* Remove devices now that I/O is cleaned up. */
55126780d9eSBradley Grove a->prev_dev_cnt =
55226780d9eSBradley Grove esas2r_targ_db_get_tgt_cnt(a);
55326780d9eSBradley Grove esas2r_targ_db_remove_all(a, false);
55426780d9eSBradley Grove }
55526780d9eSBradley Grove }
55626780d9eSBradley Grove
55726780d9eSBradley Grove a->int_mask = 0;
55826780d9eSBradley Grove }
55926780d9eSBradley Grove }
56026780d9eSBradley Grove
esas2r_handle_chip_rst_during_tasklet(struct esas2r_adapter * a)56126780d9eSBradley Grove static void esas2r_handle_chip_rst_during_tasklet(struct esas2r_adapter *a)
56226780d9eSBradley Grove {
5639588d24eSBradley Grove while (test_bit(AF_CHPRST_DETECTED, &a->flags)) {
56426780d9eSBradley Grove /*
56526780d9eSBradley Grove * Balance the enable in esas2r_initadapter_hw.
56626780d9eSBradley Grove * Esas2r_power_down already took care of it for power
56726780d9eSBradley Grove * management.
56826780d9eSBradley Grove */
5699588d24eSBradley Grove if (!test_bit(AF_DEGRADED_MODE, &a->flags) &&
5709588d24eSBradley Grove !test_bit(AF_POWER_MGT, &a->flags))
57126780d9eSBradley Grove esas2r_disable_chip_interrupts(a);
57226780d9eSBradley Grove
57326780d9eSBradley Grove /* Reinitialize the chip. */
57426780d9eSBradley Grove esas2r_check_adapter(a);
57526780d9eSBradley Grove esas2r_init_adapter_hw(a, 0);
57626780d9eSBradley Grove
5779588d24eSBradley Grove if (test_bit(AF_CHPRST_NEEDED, &a->flags))
57826780d9eSBradley Grove break;
57926780d9eSBradley Grove
5809588d24eSBradley Grove if (test_bit(AF_POWER_MGT, &a->flags)) {
58126780d9eSBradley Grove /* Recovery from power management. */
5829588d24eSBradley Grove if (test_bit(AF_FIRST_INIT, &a->flags)) {
58326780d9eSBradley Grove /* Chip reset during normal power up */
58426780d9eSBradley Grove esas2r_log(ESAS2R_LOG_CRIT,
58526780d9eSBradley Grove "The firmware was reset during a normal power-up sequence");
58626780d9eSBradley Grove } else {
58726780d9eSBradley Grove /* Deferred power up complete. */
5889588d24eSBradley Grove clear_bit(AF_POWER_MGT, &a->flags);
58926780d9eSBradley Grove esas2r_send_reset_ae(a, true);
59026780d9eSBradley Grove }
59126780d9eSBradley Grove } else {
59226780d9eSBradley Grove /* Recovery from online chip reset. */
5939588d24eSBradley Grove if (test_bit(AF_FIRST_INIT, &a->flags)) {
59426780d9eSBradley Grove /* Chip reset during driver load */
59526780d9eSBradley Grove } else {
59626780d9eSBradley Grove /* Chip reset after driver load */
59726780d9eSBradley Grove esas2r_send_reset_ae(a, false);
59826780d9eSBradley Grove }
59926780d9eSBradley Grove
60026780d9eSBradley Grove esas2r_log(ESAS2R_LOG_CRIT,
60126780d9eSBradley Grove "Recovering from a chip reset while the chip was online");
60226780d9eSBradley Grove }
60326780d9eSBradley Grove
6049588d24eSBradley Grove clear_bit(AF_CHPRST_STARTED, &a->flags);
60526780d9eSBradley Grove esas2r_enable_chip_interrupts(a);
60626780d9eSBradley Grove
60726780d9eSBradley Grove /*
60826780d9eSBradley Grove * Clear this flag last! this indicates that the chip has been
60926780d9eSBradley Grove * reset already during initialization.
61026780d9eSBradley Grove */
6119588d24eSBradley Grove clear_bit(AF_CHPRST_DETECTED, &a->flags);
61226780d9eSBradley Grove }
61326780d9eSBradley Grove }
61426780d9eSBradley Grove
61526780d9eSBradley Grove
61626780d9eSBradley Grove /* Perform deferred tasks when chip interrupts are disabled */
esas2r_do_tasklet_tasks(struct esas2r_adapter * a)61726780d9eSBradley Grove void esas2r_do_tasklet_tasks(struct esas2r_adapter *a)
61826780d9eSBradley Grove {
6199588d24eSBradley Grove
6209588d24eSBradley Grove if (test_bit(AF_CHPRST_NEEDED, &a->flags) ||
6219588d24eSBradley Grove test_bit(AF_CHPRST_DETECTED, &a->flags)) {
6229588d24eSBradley Grove if (test_bit(AF_CHPRST_NEEDED, &a->flags))
62326780d9eSBradley Grove esas2r_chip_rst_needed_during_tasklet(a);
62426780d9eSBradley Grove
62526780d9eSBradley Grove esas2r_handle_chip_rst_during_tasklet(a);
62626780d9eSBradley Grove }
62726780d9eSBradley Grove
6289588d24eSBradley Grove if (test_bit(AF_BUSRST_NEEDED, &a->flags)) {
62926780d9eSBradley Grove esas2r_hdebug("hard resetting bus");
63026780d9eSBradley Grove
6319588d24eSBradley Grove clear_bit(AF_BUSRST_NEEDED, &a->flags);
63226780d9eSBradley Grove
6339588d24eSBradley Grove if (test_bit(AF_FLASHING, &a->flags))
6349588d24eSBradley Grove set_bit(AF_BUSRST_DETECTED, &a->flags);
63526780d9eSBradley Grove else
63626780d9eSBradley Grove esas2r_write_register_dword(a, MU_DOORBELL_IN,
63726780d9eSBradley Grove DRBL_RESET_BUS);
63826780d9eSBradley Grove }
63926780d9eSBradley Grove
6409588d24eSBradley Grove if (test_bit(AF_BUSRST_DETECTED, &a->flags)) {
64126780d9eSBradley Grove esas2r_process_bus_reset(a);
64226780d9eSBradley Grove
64326780d9eSBradley Grove esas2r_log_dev(ESAS2R_LOG_WARN,
64426780d9eSBradley Grove &(a->host->shost_gendev),
64526780d9eSBradley Grove "scsi_report_bus_reset() called");
64626780d9eSBradley Grove
64726780d9eSBradley Grove scsi_report_bus_reset(a->host, 0);
64826780d9eSBradley Grove
6499588d24eSBradley Grove clear_bit(AF_BUSRST_DETECTED, &a->flags);
6509588d24eSBradley Grove clear_bit(AF_BUSRST_PENDING, &a->flags);
65126780d9eSBradley Grove
65226780d9eSBradley Grove esas2r_log(ESAS2R_LOG_WARN, "Bus reset complete");
65326780d9eSBradley Grove }
65426780d9eSBradley Grove
6559588d24eSBradley Grove if (test_bit(AF_PORT_CHANGE, &a->flags)) {
6569588d24eSBradley Grove clear_bit(AF_PORT_CHANGE, &a->flags);
65726780d9eSBradley Grove
65826780d9eSBradley Grove esas2r_targ_db_report_changes(a);
65926780d9eSBradley Grove }
66026780d9eSBradley Grove
66126780d9eSBradley Grove if (atomic_read(&a->disable_cnt) == 0)
66226780d9eSBradley Grove esas2r_do_deferred_processes(a);
66326780d9eSBradley Grove }
66426780d9eSBradley Grove
esas2r_doorbell_interrupt(struct esas2r_adapter * a,u32 doorbell)66526780d9eSBradley Grove static void esas2r_doorbell_interrupt(struct esas2r_adapter *a, u32 doorbell)
66626780d9eSBradley Grove {
66726780d9eSBradley Grove if (!(doorbell & DRBL_FORCE_INT)) {
66826780d9eSBradley Grove esas2r_trace_enter();
66926780d9eSBradley Grove esas2r_trace("doorbell: %x", doorbell);
67026780d9eSBradley Grove }
67126780d9eSBradley Grove
67226780d9eSBradley Grove /* First clear the doorbell bits */
67326780d9eSBradley Grove esas2r_write_register_dword(a, MU_DOORBELL_OUT, doorbell);
67426780d9eSBradley Grove
67526780d9eSBradley Grove if (doorbell & DRBL_RESET_BUS)
6769588d24eSBradley Grove set_bit(AF_BUSRST_DETECTED, &a->flags);
67726780d9eSBradley Grove
67826780d9eSBradley Grove if (doorbell & DRBL_FORCE_INT)
6799588d24eSBradley Grove clear_bit(AF_HEARTBEAT, &a->flags);
68026780d9eSBradley Grove
68126780d9eSBradley Grove if (doorbell & DRBL_PANIC_REASON_MASK) {
68226780d9eSBradley Grove esas2r_hdebug("*** Firmware Panic ***");
68326780d9eSBradley Grove esas2r_log(ESAS2R_LOG_CRIT, "The firmware has panicked");
68426780d9eSBradley Grove }
68526780d9eSBradley Grove
68626780d9eSBradley Grove if (doorbell & DRBL_FW_RESET) {
6879588d24eSBradley Grove set_bit(AF2_COREDUMP_AVAIL, &a->flags2);
68826780d9eSBradley Grove esas2r_local_reset_adapter(a);
68926780d9eSBradley Grove }
69026780d9eSBradley Grove
691*6abf98deSLee Jones if (!(doorbell & DRBL_FORCE_INT)) {
69226780d9eSBradley Grove esas2r_trace_exit();
69326780d9eSBradley Grove }
694*6abf98deSLee Jones }
69526780d9eSBradley Grove
esas2r_force_interrupt(struct esas2r_adapter * a)69626780d9eSBradley Grove void esas2r_force_interrupt(struct esas2r_adapter *a)
69726780d9eSBradley Grove {
69826780d9eSBradley Grove esas2r_write_register_dword(a, MU_DOORBELL_IN, DRBL_FORCE_INT |
69926780d9eSBradley Grove DRBL_DRV_VER);
70026780d9eSBradley Grove }
70126780d9eSBradley Grove
70226780d9eSBradley Grove
esas2r_lun_event(struct esas2r_adapter * a,union atto_vda_ae * ae,u16 target,u32 length)70326780d9eSBradley Grove static void esas2r_lun_event(struct esas2r_adapter *a, union atto_vda_ae *ae,
70426780d9eSBradley Grove u16 target, u32 length)
70526780d9eSBradley Grove {
70626780d9eSBradley Grove struct esas2r_target *t = a->targetdb + target;
70726780d9eSBradley Grove u32 cplen = length;
70826780d9eSBradley Grove unsigned long flags;
70926780d9eSBradley Grove
71026780d9eSBradley Grove if (cplen > sizeof(t->lu_event))
71126780d9eSBradley Grove cplen = sizeof(t->lu_event);
71226780d9eSBradley Grove
71326780d9eSBradley Grove esas2r_trace("ae->lu.dwevent: %x", ae->lu.dwevent);
71426780d9eSBradley Grove esas2r_trace("ae->lu.bystate: %x", ae->lu.bystate);
71526780d9eSBradley Grove
71626780d9eSBradley Grove spin_lock_irqsave(&a->mem_lock, flags);
71726780d9eSBradley Grove
71826780d9eSBradley Grove t->new_target_state = TS_INVALID;
71926780d9eSBradley Grove
72026780d9eSBradley Grove if (ae->lu.dwevent & VDAAE_LU_LOST) {
72126780d9eSBradley Grove t->new_target_state = TS_NOT_PRESENT;
72226780d9eSBradley Grove } else {
72326780d9eSBradley Grove switch (ae->lu.bystate) {
72426780d9eSBradley Grove case VDAAE_LU_NOT_PRESENT:
72526780d9eSBradley Grove case VDAAE_LU_OFFLINE:
72626780d9eSBradley Grove case VDAAE_LU_DELETED:
72726780d9eSBradley Grove case VDAAE_LU_FACTORY_DISABLED:
72826780d9eSBradley Grove t->new_target_state = TS_NOT_PRESENT;
72926780d9eSBradley Grove break;
73026780d9eSBradley Grove
73126780d9eSBradley Grove case VDAAE_LU_ONLINE:
73226780d9eSBradley Grove case VDAAE_LU_DEGRADED:
73326780d9eSBradley Grove t->new_target_state = TS_PRESENT;
73426780d9eSBradley Grove break;
73526780d9eSBradley Grove }
73626780d9eSBradley Grove }
73726780d9eSBradley Grove
73826780d9eSBradley Grove if (t->new_target_state != TS_INVALID) {
73926780d9eSBradley Grove memcpy(&t->lu_event, &ae->lu, cplen);
74026780d9eSBradley Grove
74126780d9eSBradley Grove esas2r_disc_queue_event(a, DCDE_DEV_CHANGE);
74226780d9eSBradley Grove }
74326780d9eSBradley Grove
74426780d9eSBradley Grove spin_unlock_irqrestore(&a->mem_lock, flags);
74526780d9eSBradley Grove }
74626780d9eSBradley Grove
74726780d9eSBradley Grove
74826780d9eSBradley Grove
esas2r_ae_complete(struct esas2r_adapter * a,struct esas2r_request * rq)74926780d9eSBradley Grove void esas2r_ae_complete(struct esas2r_adapter *a, struct esas2r_request *rq)
75026780d9eSBradley Grove {
75126780d9eSBradley Grove union atto_vda_ae *ae =
75226780d9eSBradley Grove (union atto_vda_ae *)rq->vda_rsp_data->ae_data.event_data;
75326780d9eSBradley Grove u32 length = le32_to_cpu(rq->func_rsp.ae_rsp.length);
75426780d9eSBradley Grove union atto_vda_ae *last =
75526780d9eSBradley Grove (union atto_vda_ae *)(rq->vda_rsp_data->ae_data.event_data
75626780d9eSBradley Grove + length);
75726780d9eSBradley Grove
75826780d9eSBradley Grove esas2r_trace_enter();
75926780d9eSBradley Grove esas2r_trace("length: %d", length);
76026780d9eSBradley Grove
76126780d9eSBradley Grove if (length > sizeof(struct atto_vda_ae_data)
76226780d9eSBradley Grove || (length & 3) != 0
76326780d9eSBradley Grove || length == 0) {
76426780d9eSBradley Grove esas2r_log(ESAS2R_LOG_WARN,
76526780d9eSBradley Grove "The AE request response length (%p) is too long: %d",
76626780d9eSBradley Grove rq, length);
76726780d9eSBradley Grove
76826780d9eSBradley Grove esas2r_hdebug("aereq->length (0x%x) too long", length);
76926780d9eSBradley Grove esas2r_bugon();
77026780d9eSBradley Grove
77126780d9eSBradley Grove last = ae;
77226780d9eSBradley Grove }
77326780d9eSBradley Grove
77426780d9eSBradley Grove while (ae < last) {
77526780d9eSBradley Grove u16 target;
77626780d9eSBradley Grove
77726780d9eSBradley Grove esas2r_trace("ae: %p", ae);
77826780d9eSBradley Grove esas2r_trace("ae->hdr: %p", &(ae->hdr));
77926780d9eSBradley Grove
78026780d9eSBradley Grove length = ae->hdr.bylength;
78126780d9eSBradley Grove
78226780d9eSBradley Grove if (length > (u32)((u8 *)last - (u8 *)ae)
78326780d9eSBradley Grove || (length & 3) != 0
78426780d9eSBradley Grove || length == 0) {
78526780d9eSBradley Grove esas2r_log(ESAS2R_LOG_CRIT,
78626780d9eSBradley Grove "the async event length is invalid (%p): %d",
78726780d9eSBradley Grove ae, length);
78826780d9eSBradley Grove
78926780d9eSBradley Grove esas2r_hdebug("ae->hdr.length (0x%x) invalid", length);
79026780d9eSBradley Grove esas2r_bugon();
79126780d9eSBradley Grove
79226780d9eSBradley Grove break;
79326780d9eSBradley Grove }
79426780d9eSBradley Grove
79526780d9eSBradley Grove esas2r_nuxi_ae_data(ae);
79626780d9eSBradley Grove
79726780d9eSBradley Grove esas2r_queue_fw_event(a, fw_event_vda_ae, ae,
79826780d9eSBradley Grove sizeof(union atto_vda_ae));
79926780d9eSBradley Grove
80026780d9eSBradley Grove switch (ae->hdr.bytype) {
80126780d9eSBradley Grove case VDAAE_HDR_TYPE_RAID:
80226780d9eSBradley Grove
80326780d9eSBradley Grove if (ae->raid.dwflags & (VDAAE_GROUP_STATE
80426780d9eSBradley Grove | VDAAE_RBLD_STATE
80526780d9eSBradley Grove | VDAAE_MEMBER_CHG
80626780d9eSBradley Grove | VDAAE_PART_CHG)) {
80726780d9eSBradley Grove esas2r_log(ESAS2R_LOG_INFO,
80826780d9eSBradley Grove "RAID event received - name:%s rebuild_state:%d group_state:%d",
80926780d9eSBradley Grove ae->raid.acname,
81026780d9eSBradley Grove ae->raid.byrebuild_state,
81126780d9eSBradley Grove ae->raid.bygroup_state);
81226780d9eSBradley Grove }
81326780d9eSBradley Grove
81426780d9eSBradley Grove break;
81526780d9eSBradley Grove
81626780d9eSBradley Grove case VDAAE_HDR_TYPE_LU:
81726780d9eSBradley Grove esas2r_log(ESAS2R_LOG_INFO,
81826780d9eSBradley Grove "LUN event received: event:%d target_id:%d LUN:%d state:%d",
81926780d9eSBradley Grove ae->lu.dwevent,
82026780d9eSBradley Grove ae->lu.id.tgtlun.wtarget_id,
82126780d9eSBradley Grove ae->lu.id.tgtlun.bylun,
82226780d9eSBradley Grove ae->lu.bystate);
82326780d9eSBradley Grove
82426780d9eSBradley Grove target = ae->lu.id.tgtlun.wtarget_id;
82526780d9eSBradley Grove
82626780d9eSBradley Grove if (target < ESAS2R_MAX_TARGETS)
82726780d9eSBradley Grove esas2r_lun_event(a, ae, target, length);
82826780d9eSBradley Grove
82926780d9eSBradley Grove break;
83026780d9eSBradley Grove
83126780d9eSBradley Grove case VDAAE_HDR_TYPE_DISK:
83226780d9eSBradley Grove esas2r_log(ESAS2R_LOG_INFO, "Disk event received");
83326780d9eSBradley Grove break;
83426780d9eSBradley Grove
83526780d9eSBradley Grove default:
83626780d9eSBradley Grove
83726780d9eSBradley Grove /* Silently ignore the rest and let the apps deal with
83826780d9eSBradley Grove * them.
83926780d9eSBradley Grove */
84026780d9eSBradley Grove
84126780d9eSBradley Grove break;
84226780d9eSBradley Grove }
84326780d9eSBradley Grove
84426780d9eSBradley Grove ae = (union atto_vda_ae *)((u8 *)ae + length);
84526780d9eSBradley Grove }
84626780d9eSBradley Grove
84726780d9eSBradley Grove /* Now requeue it. */
84826780d9eSBradley Grove esas2r_start_ae_request(a, rq);
84926780d9eSBradley Grove esas2r_trace_exit();
85026780d9eSBradley Grove }
85126780d9eSBradley Grove
85226780d9eSBradley Grove /* Send an asynchronous event for a chip reset or power management. */
esas2r_send_reset_ae(struct esas2r_adapter * a,bool pwr_mgt)85326780d9eSBradley Grove void esas2r_send_reset_ae(struct esas2r_adapter *a, bool pwr_mgt)
85426780d9eSBradley Grove {
85526780d9eSBradley Grove struct atto_vda_ae_hdr ae;
85626780d9eSBradley Grove
85726780d9eSBradley Grove if (pwr_mgt)
85826780d9eSBradley Grove ae.bytype = VDAAE_HDR_TYPE_PWRMGT;
85926780d9eSBradley Grove else
86026780d9eSBradley Grove ae.bytype = VDAAE_HDR_TYPE_RESET;
86126780d9eSBradley Grove
86226780d9eSBradley Grove ae.byversion = VDAAE_HDR_VER_0;
86326780d9eSBradley Grove ae.byflags = 0;
86426780d9eSBradley Grove ae.bylength = (u8)sizeof(struct atto_vda_ae_hdr);
86526780d9eSBradley Grove
866*6abf98deSLee Jones if (pwr_mgt) {
86726780d9eSBradley Grove esas2r_hdebug("*** sending power management AE ***");
868*6abf98deSLee Jones } else {
86926780d9eSBradley Grove esas2r_hdebug("*** sending reset AE ***");
870*6abf98deSLee Jones }
87126780d9eSBradley Grove
87226780d9eSBradley Grove esas2r_queue_fw_event(a, fw_event_vda_ae, &ae,
87326780d9eSBradley Grove sizeof(union atto_vda_ae));
87426780d9eSBradley Grove }
87526780d9eSBradley Grove
esas2r_dummy_complete(struct esas2r_adapter * a,struct esas2r_request * rq)87626780d9eSBradley Grove void esas2r_dummy_complete(struct esas2r_adapter *a, struct esas2r_request *rq)
87726780d9eSBradley Grove {}
87826780d9eSBradley Grove
esas2r_check_req_rsp_sense(struct esas2r_adapter * a,struct esas2r_request * rq)87926780d9eSBradley Grove static void esas2r_check_req_rsp_sense(struct esas2r_adapter *a,
88026780d9eSBradley Grove struct esas2r_request *rq)
88126780d9eSBradley Grove {
88226780d9eSBradley Grove u8 snslen, snslen2;
88326780d9eSBradley Grove
88426780d9eSBradley Grove snslen = snslen2 = rq->func_rsp.scsi_rsp.sense_len;
88526780d9eSBradley Grove
88626780d9eSBradley Grove if (snslen > rq->sense_len)
88726780d9eSBradley Grove snslen = rq->sense_len;
88826780d9eSBradley Grove
88926780d9eSBradley Grove if (snslen) {
89026780d9eSBradley Grove if (rq->sense_buf)
89126780d9eSBradley Grove memcpy(rq->sense_buf, rq->data_buf, snslen);
89226780d9eSBradley Grove else
89326780d9eSBradley Grove rq->sense_buf = (u8 *)rq->data_buf;
89426780d9eSBradley Grove
89526780d9eSBradley Grove /* See about possible sense data */
89626780d9eSBradley Grove if (snslen2 > 0x0c) {
89726780d9eSBradley Grove u8 *s = (u8 *)rq->data_buf;
89826780d9eSBradley Grove
89926780d9eSBradley Grove esas2r_trace_enter();
90026780d9eSBradley Grove
90126780d9eSBradley Grove /* Report LUNS data has changed */
90226780d9eSBradley Grove if (s[0x0c] == 0x3f && s[0x0d] == 0x0E) {
90326780d9eSBradley Grove esas2r_trace("rq->target_id: %d",
90426780d9eSBradley Grove rq->target_id);
90526780d9eSBradley Grove esas2r_target_state_changed(a, rq->target_id,
90626780d9eSBradley Grove TS_LUN_CHANGE);
90726780d9eSBradley Grove }
90826780d9eSBradley Grove
90926780d9eSBradley Grove esas2r_trace("add_sense_key=%x", s[0x0c]);
91026780d9eSBradley Grove esas2r_trace("add_sense_qual=%x", s[0x0d]);
91126780d9eSBradley Grove esas2r_trace_exit();
91226780d9eSBradley Grove }
91326780d9eSBradley Grove }
91426780d9eSBradley Grove
91526780d9eSBradley Grove rq->sense_len = snslen;
91626780d9eSBradley Grove }
91726780d9eSBradley Grove
91826780d9eSBradley Grove
esas2r_complete_request(struct esas2r_adapter * a,struct esas2r_request * rq)91926780d9eSBradley Grove void esas2r_complete_request(struct esas2r_adapter *a,
92026780d9eSBradley Grove struct esas2r_request *rq)
92126780d9eSBradley Grove {
92226780d9eSBradley Grove if (rq->vrq->scsi.function == VDA_FUNC_FLASH
92326780d9eSBradley Grove && rq->vrq->flash.sub_func == VDA_FLASH_COMMIT)
9249588d24eSBradley Grove clear_bit(AF_FLASHING, &a->flags);
92526780d9eSBradley Grove
92626780d9eSBradley Grove /* See if we setup a callback to do special processing */
92726780d9eSBradley Grove
92826780d9eSBradley Grove if (rq->interrupt_cb) {
92926780d9eSBradley Grove (*rq->interrupt_cb)(a, rq);
93026780d9eSBradley Grove
93126780d9eSBradley Grove if (rq->req_stat == RS_PENDING) {
93226780d9eSBradley Grove esas2r_start_request(a, rq);
93326780d9eSBradley Grove return;
93426780d9eSBradley Grove }
93526780d9eSBradley Grove }
93626780d9eSBradley Grove
93726780d9eSBradley Grove if (likely(rq->vrq->scsi.function == VDA_FUNC_SCSI)
93826780d9eSBradley Grove && unlikely(rq->req_stat != RS_SUCCESS)) {
93926780d9eSBradley Grove esas2r_check_req_rsp_sense(a, rq);
94026780d9eSBradley Grove esas2r_log_request_failure(a, rq);
94126780d9eSBradley Grove }
94226780d9eSBradley Grove
94326780d9eSBradley Grove (*rq->comp_cb)(a, rq);
94426780d9eSBradley Grove }
945