162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Aic94xx SAS/SATA driver SCB management.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2005 Adaptec, Inc.  All rights reserved.
662306a36Sopenharmony_ci * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/gfp.h>
1062306a36Sopenharmony_ci#include <scsi/scsi_host.h>
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include "aic94xx.h"
1362306a36Sopenharmony_ci#include "aic94xx_reg.h"
1462306a36Sopenharmony_ci#include "aic94xx_hwi.h"
1562306a36Sopenharmony_ci#include "aic94xx_seq.h"
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#include "aic94xx_dump.h"
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci/* ---------- EMPTY SCB ---------- */
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#define DL_PHY_MASK      7
2262306a36Sopenharmony_ci#define BYTES_DMAED      0
2362306a36Sopenharmony_ci#define PRIMITIVE_RECVD  0x08
2462306a36Sopenharmony_ci#define PHY_EVENT        0x10
2562306a36Sopenharmony_ci#define LINK_RESET_ERROR 0x18
2662306a36Sopenharmony_ci#define TIMER_EVENT      0x20
2762306a36Sopenharmony_ci#define REQ_TASK_ABORT   0xF0
2862306a36Sopenharmony_ci#define REQ_DEVICE_RESET 0xF1
2962306a36Sopenharmony_ci#define SIGNAL_NCQ_ERROR 0xF2
3062306a36Sopenharmony_ci#define CLEAR_NCQ_ERROR  0xF3
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci#define PHY_EVENTS_STATUS (CURRENT_LOSS_OF_SIGNAL | CURRENT_OOB_DONE   \
3362306a36Sopenharmony_ci			   | CURRENT_SPINUP_HOLD | CURRENT_GTO_TIMEOUT \
3462306a36Sopenharmony_ci			   | CURRENT_OOB_ERROR)
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_cistatic void get_lrate_mode(struct asd_phy *phy, u8 oob_mode)
3762306a36Sopenharmony_ci{
3862306a36Sopenharmony_ci	struct sas_phy *sas_phy = phy->sas_phy.phy;
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci	switch (oob_mode & 7) {
4162306a36Sopenharmony_ci	case PHY_SPEED_60:
4262306a36Sopenharmony_ci		/* FIXME: sas transport class doesn't have this */
4362306a36Sopenharmony_ci		phy->sas_phy.linkrate = SAS_LINK_RATE_6_0_GBPS;
4462306a36Sopenharmony_ci		phy->sas_phy.phy->negotiated_linkrate = SAS_LINK_RATE_6_0_GBPS;
4562306a36Sopenharmony_ci		break;
4662306a36Sopenharmony_ci	case PHY_SPEED_30:
4762306a36Sopenharmony_ci		phy->sas_phy.linkrate = SAS_LINK_RATE_3_0_GBPS;
4862306a36Sopenharmony_ci		phy->sas_phy.phy->negotiated_linkrate = SAS_LINK_RATE_3_0_GBPS;
4962306a36Sopenharmony_ci		break;
5062306a36Sopenharmony_ci	case PHY_SPEED_15:
5162306a36Sopenharmony_ci		phy->sas_phy.linkrate = SAS_LINK_RATE_1_5_GBPS;
5262306a36Sopenharmony_ci		phy->sas_phy.phy->negotiated_linkrate = SAS_LINK_RATE_1_5_GBPS;
5362306a36Sopenharmony_ci		break;
5462306a36Sopenharmony_ci	}
5562306a36Sopenharmony_ci	sas_phy->negotiated_linkrate = phy->sas_phy.linkrate;
5662306a36Sopenharmony_ci	sas_phy->maximum_linkrate_hw = SAS_LINK_RATE_3_0_GBPS;
5762306a36Sopenharmony_ci	sas_phy->minimum_linkrate_hw = SAS_LINK_RATE_1_5_GBPS;
5862306a36Sopenharmony_ci	sas_phy->maximum_linkrate = phy->phy_desc->max_sas_lrate;
5962306a36Sopenharmony_ci	sas_phy->minimum_linkrate = phy->phy_desc->min_sas_lrate;
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	if (oob_mode & SAS_MODE)
6262306a36Sopenharmony_ci		phy->sas_phy.oob_mode = SAS_OOB_MODE;
6362306a36Sopenharmony_ci	else if (oob_mode & SATA_MODE)
6462306a36Sopenharmony_ci		phy->sas_phy.oob_mode = SATA_OOB_MODE;
6562306a36Sopenharmony_ci}
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_cistatic void asd_phy_event_tasklet(struct asd_ascb *ascb,
6862306a36Sopenharmony_ci					 struct done_list_struct *dl)
6962306a36Sopenharmony_ci{
7062306a36Sopenharmony_ci	struct asd_ha_struct *asd_ha = ascb->ha;
7162306a36Sopenharmony_ci	int phy_id = dl->status_block[0] & DL_PHY_MASK;
7262306a36Sopenharmony_ci	struct asd_phy *phy = &asd_ha->phys[phy_id];
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	u8 oob_status = dl->status_block[1] & PHY_EVENTS_STATUS;
7562306a36Sopenharmony_ci	u8 oob_mode   = dl->status_block[2];
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	switch (oob_status) {
7862306a36Sopenharmony_ci	case CURRENT_LOSS_OF_SIGNAL:
7962306a36Sopenharmony_ci		/* directly attached device was removed */
8062306a36Sopenharmony_ci		ASD_DPRINTK("phy%d: device unplugged\n", phy_id);
8162306a36Sopenharmony_ci		asd_turn_led(asd_ha, phy_id, 0);
8262306a36Sopenharmony_ci		sas_phy_disconnected(&phy->sas_phy);
8362306a36Sopenharmony_ci		sas_notify_phy_event(&phy->sas_phy, PHYE_LOSS_OF_SIGNAL,
8462306a36Sopenharmony_ci				     GFP_ATOMIC);
8562306a36Sopenharmony_ci		break;
8662306a36Sopenharmony_ci	case CURRENT_OOB_DONE:
8762306a36Sopenharmony_ci		/* hot plugged device */
8862306a36Sopenharmony_ci		asd_turn_led(asd_ha, phy_id, 1);
8962306a36Sopenharmony_ci		get_lrate_mode(phy, oob_mode);
9062306a36Sopenharmony_ci		ASD_DPRINTK("phy%d device plugged: lrate:0x%x, proto:0x%x\n",
9162306a36Sopenharmony_ci			    phy_id, phy->sas_phy.linkrate, phy->sas_phy.iproto);
9262306a36Sopenharmony_ci		sas_notify_phy_event(&phy->sas_phy, PHYE_OOB_DONE, GFP_ATOMIC);
9362306a36Sopenharmony_ci		break;
9462306a36Sopenharmony_ci	case CURRENT_SPINUP_HOLD:
9562306a36Sopenharmony_ci		/* hot plug SATA, no COMWAKE sent */
9662306a36Sopenharmony_ci		asd_turn_led(asd_ha, phy_id, 1);
9762306a36Sopenharmony_ci		sas_notify_phy_event(&phy->sas_phy, PHYE_SPINUP_HOLD,
9862306a36Sopenharmony_ci				     GFP_ATOMIC);
9962306a36Sopenharmony_ci		break;
10062306a36Sopenharmony_ci	case CURRENT_GTO_TIMEOUT:
10162306a36Sopenharmony_ci	case CURRENT_OOB_ERROR:
10262306a36Sopenharmony_ci		ASD_DPRINTK("phy%d error while OOB: oob status:0x%x\n", phy_id,
10362306a36Sopenharmony_ci			    dl->status_block[1]);
10462306a36Sopenharmony_ci		asd_turn_led(asd_ha, phy_id, 0);
10562306a36Sopenharmony_ci		sas_phy_disconnected(&phy->sas_phy);
10662306a36Sopenharmony_ci		sas_notify_phy_event(&phy->sas_phy, PHYE_OOB_ERROR, GFP_ATOMIC);
10762306a36Sopenharmony_ci		break;
10862306a36Sopenharmony_ci	}
10962306a36Sopenharmony_ci}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci/* If phys are enabled sparsely, this will do the right thing. */
11262306a36Sopenharmony_cistatic unsigned ord_phy(struct asd_ha_struct *asd_ha, struct asd_phy *phy)
11362306a36Sopenharmony_ci{
11462306a36Sopenharmony_ci	u8 enabled_mask = asd_ha->hw_prof.enabled_phys;
11562306a36Sopenharmony_ci	int i, k = 0;
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	for_each_phy(enabled_mask, enabled_mask, i) {
11862306a36Sopenharmony_ci		if (&asd_ha->phys[i] == phy)
11962306a36Sopenharmony_ci			return k;
12062306a36Sopenharmony_ci		k++;
12162306a36Sopenharmony_ci	}
12262306a36Sopenharmony_ci	return 0;
12362306a36Sopenharmony_ci}
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci/**
12662306a36Sopenharmony_ci * asd_get_attached_sas_addr -- extract/generate attached SAS address
12762306a36Sopenharmony_ci * @phy: pointer to asd_phy
12862306a36Sopenharmony_ci * @sas_addr: pointer to buffer where the SAS address is to be written
12962306a36Sopenharmony_ci *
13062306a36Sopenharmony_ci * This function extracts the SAS address from an IDENTIFY frame
13162306a36Sopenharmony_ci * received.  If OOB is SATA, then a SAS address is generated from the
13262306a36Sopenharmony_ci * HA tables.
13362306a36Sopenharmony_ci *
13462306a36Sopenharmony_ci * LOCKING: the frame_rcvd_lock needs to be held since this parses the frame
13562306a36Sopenharmony_ci * buffer.
13662306a36Sopenharmony_ci */
13762306a36Sopenharmony_cistatic void asd_get_attached_sas_addr(struct asd_phy *phy, u8 *sas_addr)
13862306a36Sopenharmony_ci{
13962306a36Sopenharmony_ci	if (phy->sas_phy.frame_rcvd[0] == 0x34
14062306a36Sopenharmony_ci	    && phy->sas_phy.oob_mode == SATA_OOB_MODE) {
14162306a36Sopenharmony_ci		struct asd_ha_struct *asd_ha = phy->sas_phy.ha->lldd_ha;
14262306a36Sopenharmony_ci		/* FIS device-to-host */
14362306a36Sopenharmony_ci		u64 addr = be64_to_cpu(*(__be64 *)phy->phy_desc->sas_addr);
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci		addr += asd_ha->hw_prof.sata_name_base + ord_phy(asd_ha, phy);
14662306a36Sopenharmony_ci		*(__be64 *)sas_addr = cpu_to_be64(addr);
14762306a36Sopenharmony_ci	} else {
14862306a36Sopenharmony_ci		struct sas_identify_frame *idframe =
14962306a36Sopenharmony_ci			(void *) phy->sas_phy.frame_rcvd;
15062306a36Sopenharmony_ci		memcpy(sas_addr, idframe->sas_addr, SAS_ADDR_SIZE);
15162306a36Sopenharmony_ci	}
15262306a36Sopenharmony_ci}
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_cistatic void asd_form_port(struct asd_ha_struct *asd_ha, struct asd_phy *phy)
15562306a36Sopenharmony_ci{
15662306a36Sopenharmony_ci	int i;
15762306a36Sopenharmony_ci	struct asd_port *free_port = NULL;
15862306a36Sopenharmony_ci	struct asd_port *port;
15962306a36Sopenharmony_ci	struct asd_sas_phy *sas_phy = &phy->sas_phy;
16062306a36Sopenharmony_ci	unsigned long flags;
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	spin_lock_irqsave(&asd_ha->asd_ports_lock, flags);
16362306a36Sopenharmony_ci	if (!phy->asd_port) {
16462306a36Sopenharmony_ci		for (i = 0; i < ASD_MAX_PHYS; i++) {
16562306a36Sopenharmony_ci			port = &asd_ha->asd_ports[i];
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci			/* Check for wide port */
16862306a36Sopenharmony_ci			if (port->num_phys > 0 &&
16962306a36Sopenharmony_ci			    memcmp(port->sas_addr, sas_phy->sas_addr,
17062306a36Sopenharmony_ci				   SAS_ADDR_SIZE) == 0 &&
17162306a36Sopenharmony_ci			    memcmp(port->attached_sas_addr,
17262306a36Sopenharmony_ci				   sas_phy->attached_sas_addr,
17362306a36Sopenharmony_ci				   SAS_ADDR_SIZE) == 0) {
17462306a36Sopenharmony_ci				break;
17562306a36Sopenharmony_ci			}
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci			/* Find a free port */
17862306a36Sopenharmony_ci			if (port->num_phys == 0 && free_port == NULL) {
17962306a36Sopenharmony_ci				free_port = port;
18062306a36Sopenharmony_ci			}
18162306a36Sopenharmony_ci		}
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci		/* Use a free port if this doesn't form a wide port */
18462306a36Sopenharmony_ci		if (i >= ASD_MAX_PHYS) {
18562306a36Sopenharmony_ci			port = free_port;
18662306a36Sopenharmony_ci			BUG_ON(!port);
18762306a36Sopenharmony_ci			memcpy(port->sas_addr, sas_phy->sas_addr,
18862306a36Sopenharmony_ci			       SAS_ADDR_SIZE);
18962306a36Sopenharmony_ci			memcpy(port->attached_sas_addr,
19062306a36Sopenharmony_ci			       sas_phy->attached_sas_addr,
19162306a36Sopenharmony_ci			       SAS_ADDR_SIZE);
19262306a36Sopenharmony_ci		}
19362306a36Sopenharmony_ci		port->num_phys++;
19462306a36Sopenharmony_ci		port->phy_mask |= (1U << sas_phy->id);
19562306a36Sopenharmony_ci		phy->asd_port = port;
19662306a36Sopenharmony_ci	}
19762306a36Sopenharmony_ci	ASD_DPRINTK("%s: updating phy_mask 0x%x for phy%d\n",
19862306a36Sopenharmony_ci		    __func__, phy->asd_port->phy_mask, sas_phy->id);
19962306a36Sopenharmony_ci	asd_update_port_links(asd_ha, phy);
20062306a36Sopenharmony_ci	spin_unlock_irqrestore(&asd_ha->asd_ports_lock, flags);
20162306a36Sopenharmony_ci}
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_cistatic void asd_deform_port(struct asd_ha_struct *asd_ha, struct asd_phy *phy)
20462306a36Sopenharmony_ci{
20562306a36Sopenharmony_ci	struct asd_port *port = phy->asd_port;
20662306a36Sopenharmony_ci	struct asd_sas_phy *sas_phy = &phy->sas_phy;
20762306a36Sopenharmony_ci	unsigned long flags;
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	spin_lock_irqsave(&asd_ha->asd_ports_lock, flags);
21062306a36Sopenharmony_ci	if (port) {
21162306a36Sopenharmony_ci		port->num_phys--;
21262306a36Sopenharmony_ci		port->phy_mask &= ~(1U << sas_phy->id);
21362306a36Sopenharmony_ci		phy->asd_port = NULL;
21462306a36Sopenharmony_ci	}
21562306a36Sopenharmony_ci	spin_unlock_irqrestore(&asd_ha->asd_ports_lock, flags);
21662306a36Sopenharmony_ci}
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_cistatic void asd_bytes_dmaed_tasklet(struct asd_ascb *ascb,
21962306a36Sopenharmony_ci				    struct done_list_struct *dl,
22062306a36Sopenharmony_ci				    int edb_id, int phy_id)
22162306a36Sopenharmony_ci{
22262306a36Sopenharmony_ci	unsigned long flags;
22362306a36Sopenharmony_ci	int edb_el = edb_id + ascb->edb_index;
22462306a36Sopenharmony_ci	struct asd_dma_tok *edb = ascb->ha->seq.edb_arr[edb_el];
22562306a36Sopenharmony_ci	struct asd_phy *phy = &ascb->ha->phys[phy_id];
22662306a36Sopenharmony_ci	u16 size = ((dl->status_block[3] & 7) << 8) | dl->status_block[2];
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	size = min(size, (u16) sizeof(phy->frame_rcvd));
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	spin_lock_irqsave(&phy->sas_phy.frame_rcvd_lock, flags);
23162306a36Sopenharmony_ci	memcpy(phy->sas_phy.frame_rcvd, edb->vaddr, size);
23262306a36Sopenharmony_ci	phy->sas_phy.frame_rcvd_size = size;
23362306a36Sopenharmony_ci	asd_get_attached_sas_addr(phy, phy->sas_phy.attached_sas_addr);
23462306a36Sopenharmony_ci	spin_unlock_irqrestore(&phy->sas_phy.frame_rcvd_lock, flags);
23562306a36Sopenharmony_ci	asd_dump_frame_rcvd(phy, dl);
23662306a36Sopenharmony_ci	asd_form_port(ascb->ha, phy);
23762306a36Sopenharmony_ci	sas_notify_port_event(&phy->sas_phy, PORTE_BYTES_DMAED, GFP_ATOMIC);
23862306a36Sopenharmony_ci}
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_cistatic void asd_link_reset_err_tasklet(struct asd_ascb *ascb,
24162306a36Sopenharmony_ci				       struct done_list_struct *dl,
24262306a36Sopenharmony_ci				       int phy_id)
24362306a36Sopenharmony_ci{
24462306a36Sopenharmony_ci	struct asd_ha_struct *asd_ha = ascb->ha;
24562306a36Sopenharmony_ci	struct sas_ha_struct *sas_ha = &asd_ha->sas_ha;
24662306a36Sopenharmony_ci	struct asd_sas_phy *sas_phy = sas_ha->sas_phy[phy_id];
24762306a36Sopenharmony_ci	struct asd_phy *phy = &asd_ha->phys[phy_id];
24862306a36Sopenharmony_ci	u8 lr_error = dl->status_block[1];
24962306a36Sopenharmony_ci	u8 retries_left = dl->status_block[2];
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	switch (lr_error) {
25262306a36Sopenharmony_ci	case 0:
25362306a36Sopenharmony_ci		ASD_DPRINTK("phy%d: Receive ID timer expired\n", phy_id);
25462306a36Sopenharmony_ci		break;
25562306a36Sopenharmony_ci	case 1:
25662306a36Sopenharmony_ci		ASD_DPRINTK("phy%d: Loss of signal\n", phy_id);
25762306a36Sopenharmony_ci		break;
25862306a36Sopenharmony_ci	case 2:
25962306a36Sopenharmony_ci		ASD_DPRINTK("phy%d: Loss of dword sync\n", phy_id);
26062306a36Sopenharmony_ci		break;
26162306a36Sopenharmony_ci	case 3:
26262306a36Sopenharmony_ci		ASD_DPRINTK("phy%d: Receive FIS timeout\n", phy_id);
26362306a36Sopenharmony_ci		break;
26462306a36Sopenharmony_ci	default:
26562306a36Sopenharmony_ci		ASD_DPRINTK("phy%d: unknown link reset error code: 0x%x\n",
26662306a36Sopenharmony_ci			    phy_id, lr_error);
26762306a36Sopenharmony_ci		break;
26862306a36Sopenharmony_ci	}
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	asd_turn_led(asd_ha, phy_id, 0);
27162306a36Sopenharmony_ci	sas_phy_disconnected(sas_phy);
27262306a36Sopenharmony_ci	asd_deform_port(asd_ha, phy);
27362306a36Sopenharmony_ci	sas_notify_port_event(sas_phy, PORTE_LINK_RESET_ERR, GFP_ATOMIC);
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	if (retries_left == 0) {
27662306a36Sopenharmony_ci		int num = 1;
27762306a36Sopenharmony_ci		struct asd_ascb *cp = asd_ascb_alloc_list(ascb->ha, &num,
27862306a36Sopenharmony_ci							  GFP_ATOMIC);
27962306a36Sopenharmony_ci		if (!cp) {
28062306a36Sopenharmony_ci			asd_printk("%s: out of memory\n", __func__);
28162306a36Sopenharmony_ci			goto out;
28262306a36Sopenharmony_ci		}
28362306a36Sopenharmony_ci		ASD_DPRINTK("phy%d: retries:0 performing link reset seq\n",
28462306a36Sopenharmony_ci			    phy_id);
28562306a36Sopenharmony_ci		asd_build_control_phy(cp, phy_id, ENABLE_PHY);
28662306a36Sopenharmony_ci		if (asd_post_ascb_list(ascb->ha, cp, 1) != 0)
28762306a36Sopenharmony_ci			asd_ascb_free(cp);
28862306a36Sopenharmony_ci	}
28962306a36Sopenharmony_ciout:
29062306a36Sopenharmony_ci	;
29162306a36Sopenharmony_ci}
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_cistatic void asd_primitive_rcvd_tasklet(struct asd_ascb *ascb,
29462306a36Sopenharmony_ci				       struct done_list_struct *dl,
29562306a36Sopenharmony_ci				       int phy_id)
29662306a36Sopenharmony_ci{
29762306a36Sopenharmony_ci	unsigned long flags;
29862306a36Sopenharmony_ci	struct sas_ha_struct *sas_ha = &ascb->ha->sas_ha;
29962306a36Sopenharmony_ci	struct asd_sas_phy *sas_phy = sas_ha->sas_phy[phy_id];
30062306a36Sopenharmony_ci	struct asd_ha_struct *asd_ha = ascb->ha;
30162306a36Sopenharmony_ci	struct asd_phy *phy = &asd_ha->phys[phy_id];
30262306a36Sopenharmony_ci	u8  reg  = dl->status_block[1];
30362306a36Sopenharmony_ci	u32 cont = dl->status_block[2] << ((reg & 3)*8);
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	reg &= ~3;
30662306a36Sopenharmony_ci	switch (reg) {
30762306a36Sopenharmony_ci	case LmPRMSTAT0BYTE0:
30862306a36Sopenharmony_ci		switch (cont) {
30962306a36Sopenharmony_ci		case LmBROADCH:
31062306a36Sopenharmony_ci		case LmBROADRVCH0:
31162306a36Sopenharmony_ci		case LmBROADRVCH1:
31262306a36Sopenharmony_ci		case LmBROADSES:
31362306a36Sopenharmony_ci			ASD_DPRINTK("phy%d: BROADCAST change received:%d\n",
31462306a36Sopenharmony_ci				    phy_id, cont);
31562306a36Sopenharmony_ci			spin_lock_irqsave(&sas_phy->sas_prim_lock, flags);
31662306a36Sopenharmony_ci			sas_phy->sas_prim = ffs(cont);
31762306a36Sopenharmony_ci			spin_unlock_irqrestore(&sas_phy->sas_prim_lock, flags);
31862306a36Sopenharmony_ci			sas_notify_port_event(sas_phy, PORTE_BROADCAST_RCVD,
31962306a36Sopenharmony_ci					      GFP_ATOMIC);
32062306a36Sopenharmony_ci			break;
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci		case LmUNKNOWNP:
32362306a36Sopenharmony_ci			ASD_DPRINTK("phy%d: unknown BREAK\n", phy_id);
32462306a36Sopenharmony_ci			break;
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci		default:
32762306a36Sopenharmony_ci			ASD_DPRINTK("phy%d: primitive reg:0x%x, cont:0x%04x\n",
32862306a36Sopenharmony_ci				    phy_id, reg, cont);
32962306a36Sopenharmony_ci			break;
33062306a36Sopenharmony_ci		}
33162306a36Sopenharmony_ci		break;
33262306a36Sopenharmony_ci	case LmPRMSTAT1BYTE0:
33362306a36Sopenharmony_ci		switch (cont) {
33462306a36Sopenharmony_ci		case LmHARDRST:
33562306a36Sopenharmony_ci			ASD_DPRINTK("phy%d: HARD_RESET primitive rcvd\n",
33662306a36Sopenharmony_ci				    phy_id);
33762306a36Sopenharmony_ci			/* The sequencer disables all phys on that port.
33862306a36Sopenharmony_ci			 * We have to re-enable the phys ourselves. */
33962306a36Sopenharmony_ci			asd_deform_port(asd_ha, phy);
34062306a36Sopenharmony_ci			sas_notify_port_event(sas_phy, PORTE_HARD_RESET,
34162306a36Sopenharmony_ci					      GFP_ATOMIC);
34262306a36Sopenharmony_ci			break;
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci		default:
34562306a36Sopenharmony_ci			ASD_DPRINTK("phy%d: primitive reg:0x%x, cont:0x%04x\n",
34662306a36Sopenharmony_ci				    phy_id, reg, cont);
34762306a36Sopenharmony_ci			break;
34862306a36Sopenharmony_ci		}
34962306a36Sopenharmony_ci		break;
35062306a36Sopenharmony_ci	default:
35162306a36Sopenharmony_ci		ASD_DPRINTK("unknown primitive register:0x%x\n",
35262306a36Sopenharmony_ci			    dl->status_block[1]);
35362306a36Sopenharmony_ci		break;
35462306a36Sopenharmony_ci	}
35562306a36Sopenharmony_ci}
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci/**
35862306a36Sopenharmony_ci * asd_invalidate_edb -- invalidate an EDB and if necessary post the ESCB
35962306a36Sopenharmony_ci * @ascb: pointer to Empty SCB
36062306a36Sopenharmony_ci * @edb_id: index [0,6] to the empty data buffer which is to be invalidated
36162306a36Sopenharmony_ci *
36262306a36Sopenharmony_ci * After an EDB has been invalidated, if all EDBs in this ESCB have been
36362306a36Sopenharmony_ci * invalidated, the ESCB is posted back to the sequencer.
36462306a36Sopenharmony_ci * Context is tasklet/IRQ.
36562306a36Sopenharmony_ci */
36662306a36Sopenharmony_civoid asd_invalidate_edb(struct asd_ascb *ascb, int edb_id)
36762306a36Sopenharmony_ci{
36862306a36Sopenharmony_ci	struct asd_seq_data *seq = &ascb->ha->seq;
36962306a36Sopenharmony_ci	struct empty_scb *escb = &ascb->scb->escb;
37062306a36Sopenharmony_ci	struct sg_el     *eb   = &escb->eb[edb_id];
37162306a36Sopenharmony_ci	struct asd_dma_tok *edb = seq->edb_arr[ascb->edb_index + edb_id];
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	memset(edb->vaddr, 0, ASD_EDB_SIZE);
37462306a36Sopenharmony_ci	eb->flags |= ELEMENT_NOT_VALID;
37562306a36Sopenharmony_ci	escb->num_valid--;
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	if (escb->num_valid == 0) {
37862306a36Sopenharmony_ci		int i;
37962306a36Sopenharmony_ci		/* ASD_DPRINTK("reposting escb: vaddr: 0x%p, "
38062306a36Sopenharmony_ci			    "dma_handle: 0x%08llx, next: 0x%08llx, "
38162306a36Sopenharmony_ci			    "index:%d, opcode:0x%02x\n",
38262306a36Sopenharmony_ci			    ascb->dma_scb.vaddr,
38362306a36Sopenharmony_ci			    (u64)ascb->dma_scb.dma_handle,
38462306a36Sopenharmony_ci			    le64_to_cpu(ascb->scb->header.next_scb),
38562306a36Sopenharmony_ci			    le16_to_cpu(ascb->scb->header.index),
38662306a36Sopenharmony_ci			    ascb->scb->header.opcode);
38762306a36Sopenharmony_ci		*/
38862306a36Sopenharmony_ci		escb->num_valid = ASD_EDBS_PER_SCB;
38962306a36Sopenharmony_ci		for (i = 0; i < ASD_EDBS_PER_SCB; i++)
39062306a36Sopenharmony_ci			escb->eb[i].flags = 0;
39162306a36Sopenharmony_ci		if (!list_empty(&ascb->list))
39262306a36Sopenharmony_ci			list_del_init(&ascb->list);
39362306a36Sopenharmony_ci		i = asd_post_escb_list(ascb->ha, ascb, 1);
39462306a36Sopenharmony_ci		if (i)
39562306a36Sopenharmony_ci			asd_printk("couldn't post escb, err:%d\n", i);
39662306a36Sopenharmony_ci	}
39762306a36Sopenharmony_ci}
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_cistatic void escb_tasklet_complete(struct asd_ascb *ascb,
40062306a36Sopenharmony_ci				  struct done_list_struct *dl)
40162306a36Sopenharmony_ci{
40262306a36Sopenharmony_ci	struct asd_ha_struct *asd_ha = ascb->ha;
40362306a36Sopenharmony_ci	struct sas_ha_struct *sas_ha = &asd_ha->sas_ha;
40462306a36Sopenharmony_ci	int edb = (dl->opcode & DL_PHY_MASK) - 1; /* [0xc1,0xc7] -> [0,6] */
40562306a36Sopenharmony_ci	u8  sb_opcode = dl->status_block[0];
40662306a36Sopenharmony_ci	int phy_id = sb_opcode & DL_PHY_MASK;
40762306a36Sopenharmony_ci	struct asd_sas_phy *sas_phy = sas_ha->sas_phy[phy_id];
40862306a36Sopenharmony_ci	struct asd_phy *phy = &asd_ha->phys[phy_id];
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	if (edb > 6 || edb < 0) {
41162306a36Sopenharmony_ci		ASD_DPRINTK("edb is 0x%x! dl->opcode is 0x%x\n",
41262306a36Sopenharmony_ci			    edb, dl->opcode);
41362306a36Sopenharmony_ci		ASD_DPRINTK("sb_opcode : 0x%x, phy_id: 0x%x\n",
41462306a36Sopenharmony_ci			    sb_opcode, phy_id);
41562306a36Sopenharmony_ci		ASD_DPRINTK("escb: vaddr: 0x%p, "
41662306a36Sopenharmony_ci			    "dma_handle: 0x%llx, next: 0x%llx, "
41762306a36Sopenharmony_ci			    "index:%d, opcode:0x%02x\n",
41862306a36Sopenharmony_ci			    ascb->dma_scb.vaddr,
41962306a36Sopenharmony_ci			    (unsigned long long)ascb->dma_scb.dma_handle,
42062306a36Sopenharmony_ci			    (unsigned long long)
42162306a36Sopenharmony_ci			    le64_to_cpu(ascb->scb->header.next_scb),
42262306a36Sopenharmony_ci			    le16_to_cpu(ascb->scb->header.index),
42362306a36Sopenharmony_ci			    ascb->scb->header.opcode);
42462306a36Sopenharmony_ci	}
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci	/* Catch these before we mask off the sb_opcode bits */
42762306a36Sopenharmony_ci	switch (sb_opcode) {
42862306a36Sopenharmony_ci	case REQ_TASK_ABORT: {
42962306a36Sopenharmony_ci		struct asd_ascb *a, *b;
43062306a36Sopenharmony_ci		u16 tc_abort;
43162306a36Sopenharmony_ci		struct domain_device *failed_dev = NULL;
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci		ASD_DPRINTK("%s: REQ_TASK_ABORT, reason=0x%X\n",
43462306a36Sopenharmony_ci			    __func__, dl->status_block[3]);
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci		/*
43762306a36Sopenharmony_ci		 * Find the task that caused the abort and abort it first.
43862306a36Sopenharmony_ci		 * The sequencer won't put anything on the done list until
43962306a36Sopenharmony_ci		 * that happens.
44062306a36Sopenharmony_ci		 */
44162306a36Sopenharmony_ci		tc_abort = *((u16*)(&dl->status_block[1]));
44262306a36Sopenharmony_ci		tc_abort = le16_to_cpu(tc_abort);
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci		list_for_each_entry_safe(a, b, &asd_ha->seq.pend_q, list) {
44562306a36Sopenharmony_ci			struct sas_task *task = a->uldd_task;
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci			if (a->tc_index != tc_abort)
44862306a36Sopenharmony_ci				continue;
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci			if (task) {
45162306a36Sopenharmony_ci				failed_dev = task->dev;
45262306a36Sopenharmony_ci				sas_task_abort(task);
45362306a36Sopenharmony_ci			} else {
45462306a36Sopenharmony_ci				ASD_DPRINTK("R_T_A for non TASK scb 0x%x\n",
45562306a36Sopenharmony_ci					    a->scb->header.opcode);
45662306a36Sopenharmony_ci			}
45762306a36Sopenharmony_ci			break;
45862306a36Sopenharmony_ci		}
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci		if (!failed_dev) {
46162306a36Sopenharmony_ci			ASD_DPRINTK("%s: Can't find task (tc=%d) to abort!\n",
46262306a36Sopenharmony_ci				    __func__, tc_abort);
46362306a36Sopenharmony_ci			goto out;
46462306a36Sopenharmony_ci		}
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci		/*
46762306a36Sopenharmony_ci		 * Now abort everything else for that device (hba?) so
46862306a36Sopenharmony_ci		 * that the EH will wake up and do something.
46962306a36Sopenharmony_ci		 */
47062306a36Sopenharmony_ci		list_for_each_entry_safe(a, b, &asd_ha->seq.pend_q, list) {
47162306a36Sopenharmony_ci			struct sas_task *task = a->uldd_task;
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci			if (task &&
47462306a36Sopenharmony_ci			    task->dev == failed_dev &&
47562306a36Sopenharmony_ci			    a->tc_index != tc_abort)
47662306a36Sopenharmony_ci				sas_task_abort(task);
47762306a36Sopenharmony_ci		}
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci		goto out;
48062306a36Sopenharmony_ci	}
48162306a36Sopenharmony_ci	case REQ_DEVICE_RESET: {
48262306a36Sopenharmony_ci		struct asd_ascb *a;
48362306a36Sopenharmony_ci		u16 conn_handle;
48462306a36Sopenharmony_ci		unsigned long flags;
48562306a36Sopenharmony_ci		struct sas_task *last_dev_task = NULL;
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci		conn_handle = *((u16*)(&dl->status_block[1]));
48862306a36Sopenharmony_ci		conn_handle = le16_to_cpu(conn_handle);
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci		ASD_DPRINTK("%s: REQ_DEVICE_RESET, reason=0x%X\n", __func__,
49162306a36Sopenharmony_ci			    dl->status_block[3]);
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci		/* Find the last pending task for the device... */
49462306a36Sopenharmony_ci		list_for_each_entry(a, &asd_ha->seq.pend_q, list) {
49562306a36Sopenharmony_ci			u16 x;
49662306a36Sopenharmony_ci			struct domain_device *dev;
49762306a36Sopenharmony_ci			struct sas_task *task = a->uldd_task;
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci			if (!task)
50062306a36Sopenharmony_ci				continue;
50162306a36Sopenharmony_ci			dev = task->dev;
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci			x = (unsigned long)dev->lldd_dev;
50462306a36Sopenharmony_ci			if (x == conn_handle)
50562306a36Sopenharmony_ci				last_dev_task = task;
50662306a36Sopenharmony_ci		}
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci		if (!last_dev_task) {
50962306a36Sopenharmony_ci			ASD_DPRINTK("%s: Device reset for idle device %d?\n",
51062306a36Sopenharmony_ci				    __func__, conn_handle);
51162306a36Sopenharmony_ci			goto out;
51262306a36Sopenharmony_ci		}
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci		/* ...and set the reset flag */
51562306a36Sopenharmony_ci		spin_lock_irqsave(&last_dev_task->task_state_lock, flags);
51662306a36Sopenharmony_ci		last_dev_task->task_state_flags |= SAS_TASK_NEED_DEV_RESET;
51762306a36Sopenharmony_ci		spin_unlock_irqrestore(&last_dev_task->task_state_lock, flags);
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci		/* Kill all pending tasks for the device */
52062306a36Sopenharmony_ci		list_for_each_entry(a, &asd_ha->seq.pend_q, list) {
52162306a36Sopenharmony_ci			u16 x;
52262306a36Sopenharmony_ci			struct domain_device *dev;
52362306a36Sopenharmony_ci			struct sas_task *task = a->uldd_task;
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci			if (!task)
52662306a36Sopenharmony_ci				continue;
52762306a36Sopenharmony_ci			dev = task->dev;
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci			x = (unsigned long)dev->lldd_dev;
53062306a36Sopenharmony_ci			if (x == conn_handle)
53162306a36Sopenharmony_ci				sas_task_abort(task);
53262306a36Sopenharmony_ci		}
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci		goto out;
53562306a36Sopenharmony_ci	}
53662306a36Sopenharmony_ci	case SIGNAL_NCQ_ERROR:
53762306a36Sopenharmony_ci		ASD_DPRINTK("%s: SIGNAL_NCQ_ERROR\n", __func__);
53862306a36Sopenharmony_ci		goto out;
53962306a36Sopenharmony_ci	case CLEAR_NCQ_ERROR:
54062306a36Sopenharmony_ci		ASD_DPRINTK("%s: CLEAR_NCQ_ERROR\n", __func__);
54162306a36Sopenharmony_ci		goto out;
54262306a36Sopenharmony_ci	}
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	sb_opcode &= ~DL_PHY_MASK;
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci	switch (sb_opcode) {
54762306a36Sopenharmony_ci	case BYTES_DMAED:
54862306a36Sopenharmony_ci		ASD_DPRINTK("%s: phy%d: BYTES_DMAED\n", __func__, phy_id);
54962306a36Sopenharmony_ci		asd_bytes_dmaed_tasklet(ascb, dl, edb, phy_id);
55062306a36Sopenharmony_ci		break;
55162306a36Sopenharmony_ci	case PRIMITIVE_RECVD:
55262306a36Sopenharmony_ci		ASD_DPRINTK("%s: phy%d: PRIMITIVE_RECVD\n", __func__,
55362306a36Sopenharmony_ci			    phy_id);
55462306a36Sopenharmony_ci		asd_primitive_rcvd_tasklet(ascb, dl, phy_id);
55562306a36Sopenharmony_ci		break;
55662306a36Sopenharmony_ci	case PHY_EVENT:
55762306a36Sopenharmony_ci		ASD_DPRINTK("%s: phy%d: PHY_EVENT\n", __func__, phy_id);
55862306a36Sopenharmony_ci		asd_phy_event_tasklet(ascb, dl);
55962306a36Sopenharmony_ci		break;
56062306a36Sopenharmony_ci	case LINK_RESET_ERROR:
56162306a36Sopenharmony_ci		ASD_DPRINTK("%s: phy%d: LINK_RESET_ERROR\n", __func__,
56262306a36Sopenharmony_ci			    phy_id);
56362306a36Sopenharmony_ci		asd_link_reset_err_tasklet(ascb, dl, phy_id);
56462306a36Sopenharmony_ci		break;
56562306a36Sopenharmony_ci	case TIMER_EVENT:
56662306a36Sopenharmony_ci		ASD_DPRINTK("%s: phy%d: TIMER_EVENT, lost dw sync\n",
56762306a36Sopenharmony_ci			    __func__, phy_id);
56862306a36Sopenharmony_ci		asd_turn_led(asd_ha, phy_id, 0);
56962306a36Sopenharmony_ci		/* the device is gone */
57062306a36Sopenharmony_ci		sas_phy_disconnected(sas_phy);
57162306a36Sopenharmony_ci		asd_deform_port(asd_ha, phy);
57262306a36Sopenharmony_ci		sas_notify_port_event(sas_phy, PORTE_TIMER_EVENT, GFP_ATOMIC);
57362306a36Sopenharmony_ci		break;
57462306a36Sopenharmony_ci	default:
57562306a36Sopenharmony_ci		ASD_DPRINTK("%s: phy%d: unknown event:0x%x\n", __func__,
57662306a36Sopenharmony_ci			    phy_id, sb_opcode);
57762306a36Sopenharmony_ci		ASD_DPRINTK("edb is 0x%x! dl->opcode is 0x%x\n",
57862306a36Sopenharmony_ci			    edb, dl->opcode);
57962306a36Sopenharmony_ci		ASD_DPRINTK("sb_opcode : 0x%x, phy_id: 0x%x\n",
58062306a36Sopenharmony_ci			    sb_opcode, phy_id);
58162306a36Sopenharmony_ci		ASD_DPRINTK("escb: vaddr: 0x%p, "
58262306a36Sopenharmony_ci			    "dma_handle: 0x%llx, next: 0x%llx, "
58362306a36Sopenharmony_ci			    "index:%d, opcode:0x%02x\n",
58462306a36Sopenharmony_ci			    ascb->dma_scb.vaddr,
58562306a36Sopenharmony_ci			    (unsigned long long)ascb->dma_scb.dma_handle,
58662306a36Sopenharmony_ci			    (unsigned long long)
58762306a36Sopenharmony_ci			    le64_to_cpu(ascb->scb->header.next_scb),
58862306a36Sopenharmony_ci			    le16_to_cpu(ascb->scb->header.index),
58962306a36Sopenharmony_ci			    ascb->scb->header.opcode);
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci		break;
59262306a36Sopenharmony_ci	}
59362306a36Sopenharmony_ciout:
59462306a36Sopenharmony_ci	asd_invalidate_edb(ascb, edb);
59562306a36Sopenharmony_ci}
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ciint asd_init_post_escbs(struct asd_ha_struct *asd_ha)
59862306a36Sopenharmony_ci{
59962306a36Sopenharmony_ci	struct asd_seq_data *seq = &asd_ha->seq;
60062306a36Sopenharmony_ci	int i;
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci	for (i = 0; i < seq->num_escbs; i++)
60362306a36Sopenharmony_ci		seq->escb_arr[i]->tasklet_complete = escb_tasklet_complete;
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci	ASD_DPRINTK("posting %d escbs\n", i);
60662306a36Sopenharmony_ci	return asd_post_escb_list(asd_ha, seq->escb_arr[0], seq->num_escbs);
60762306a36Sopenharmony_ci}
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci/* ---------- CONTROL PHY ---------- */
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci#define CONTROL_PHY_STATUS (CURRENT_DEVICE_PRESENT | CURRENT_OOB_DONE   \
61262306a36Sopenharmony_ci			    | CURRENT_SPINUP_HOLD | CURRENT_GTO_TIMEOUT \
61362306a36Sopenharmony_ci			    | CURRENT_OOB_ERROR)
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci/**
61662306a36Sopenharmony_ci * control_phy_tasklet_complete -- tasklet complete for CONTROL PHY ascb
61762306a36Sopenharmony_ci * @ascb: pointer to an ascb
61862306a36Sopenharmony_ci * @dl: pointer to the done list entry
61962306a36Sopenharmony_ci *
62062306a36Sopenharmony_ci * This function completes a CONTROL PHY scb and frees the ascb.
62162306a36Sopenharmony_ci * A note on LEDs:
62262306a36Sopenharmony_ci *  - an LED blinks if there is IO though it,
62362306a36Sopenharmony_ci *  - if a device is connected to the LED, it is lit,
62462306a36Sopenharmony_ci *  - if no device is connected to the LED, is is dimmed (off).
62562306a36Sopenharmony_ci */
62662306a36Sopenharmony_cistatic void control_phy_tasklet_complete(struct asd_ascb *ascb,
62762306a36Sopenharmony_ci					 struct done_list_struct *dl)
62862306a36Sopenharmony_ci{
62962306a36Sopenharmony_ci	struct asd_ha_struct *asd_ha = ascb->ha;
63062306a36Sopenharmony_ci	struct scb *scb = ascb->scb;
63162306a36Sopenharmony_ci	struct control_phy *control_phy = &scb->control_phy;
63262306a36Sopenharmony_ci	u8 phy_id = control_phy->phy_id;
63362306a36Sopenharmony_ci	struct asd_phy *phy = &ascb->ha->phys[phy_id];
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci	u8 status     = dl->status_block[0];
63662306a36Sopenharmony_ci	u8 oob_status = dl->status_block[1];
63762306a36Sopenharmony_ci	u8 oob_mode   = dl->status_block[2];
63862306a36Sopenharmony_ci	/* u8 oob_signals= dl->status_block[3]; */
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci	if (status != 0) {
64162306a36Sopenharmony_ci		ASD_DPRINTK("%s: phy%d status block opcode:0x%x\n",
64262306a36Sopenharmony_ci			    __func__, phy_id, status);
64362306a36Sopenharmony_ci		goto out;
64462306a36Sopenharmony_ci	}
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_ci	switch (control_phy->sub_func) {
64762306a36Sopenharmony_ci	case DISABLE_PHY:
64862306a36Sopenharmony_ci		asd_ha->hw_prof.enabled_phys &= ~(1 << phy_id);
64962306a36Sopenharmony_ci		asd_turn_led(asd_ha, phy_id, 0);
65062306a36Sopenharmony_ci		asd_control_led(asd_ha, phy_id, 0);
65162306a36Sopenharmony_ci		ASD_DPRINTK("%s: disable phy%d\n", __func__, phy_id);
65262306a36Sopenharmony_ci		break;
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci	case ENABLE_PHY:
65562306a36Sopenharmony_ci		asd_control_led(asd_ha, phy_id, 1);
65662306a36Sopenharmony_ci		if (oob_status & CURRENT_OOB_DONE) {
65762306a36Sopenharmony_ci			asd_ha->hw_prof.enabled_phys |= (1 << phy_id);
65862306a36Sopenharmony_ci			get_lrate_mode(phy, oob_mode);
65962306a36Sopenharmony_ci			asd_turn_led(asd_ha, phy_id, 1);
66062306a36Sopenharmony_ci			ASD_DPRINTK("%s: phy%d, lrate:0x%x, proto:0x%x\n",
66162306a36Sopenharmony_ci				    __func__, phy_id,phy->sas_phy.linkrate,
66262306a36Sopenharmony_ci				    phy->sas_phy.iproto);
66362306a36Sopenharmony_ci		} else if (oob_status & CURRENT_SPINUP_HOLD) {
66462306a36Sopenharmony_ci			asd_ha->hw_prof.enabled_phys |= (1 << phy_id);
66562306a36Sopenharmony_ci			asd_turn_led(asd_ha, phy_id, 1);
66662306a36Sopenharmony_ci			ASD_DPRINTK("%s: phy%d, spinup hold\n", __func__,
66762306a36Sopenharmony_ci				    phy_id);
66862306a36Sopenharmony_ci		} else if (oob_status & CURRENT_ERR_MASK) {
66962306a36Sopenharmony_ci			asd_turn_led(asd_ha, phy_id, 0);
67062306a36Sopenharmony_ci			ASD_DPRINTK("%s: phy%d: error: oob status:0x%02x\n",
67162306a36Sopenharmony_ci				    __func__, phy_id, oob_status);
67262306a36Sopenharmony_ci		} else if (oob_status & (CURRENT_HOT_PLUG_CNCT
67362306a36Sopenharmony_ci					 | CURRENT_DEVICE_PRESENT))  {
67462306a36Sopenharmony_ci			asd_ha->hw_prof.enabled_phys |= (1 << phy_id);
67562306a36Sopenharmony_ci			asd_turn_led(asd_ha, phy_id, 1);
67662306a36Sopenharmony_ci			ASD_DPRINTK("%s: phy%d: hot plug or device present\n",
67762306a36Sopenharmony_ci				    __func__, phy_id);
67862306a36Sopenharmony_ci		} else {
67962306a36Sopenharmony_ci			asd_ha->hw_prof.enabled_phys |= (1 << phy_id);
68062306a36Sopenharmony_ci			asd_turn_led(asd_ha, phy_id, 0);
68162306a36Sopenharmony_ci			ASD_DPRINTK("%s: phy%d: no device present: "
68262306a36Sopenharmony_ci				    "oob_status:0x%x\n",
68362306a36Sopenharmony_ci				    __func__, phy_id, oob_status);
68462306a36Sopenharmony_ci		}
68562306a36Sopenharmony_ci		break;
68662306a36Sopenharmony_ci	case RELEASE_SPINUP_HOLD:
68762306a36Sopenharmony_ci	case PHY_NO_OP:
68862306a36Sopenharmony_ci	case EXECUTE_HARD_RESET:
68962306a36Sopenharmony_ci		ASD_DPRINTK("%s: phy%d: sub_func:0x%x\n", __func__,
69062306a36Sopenharmony_ci			    phy_id, control_phy->sub_func);
69162306a36Sopenharmony_ci		/* XXX finish */
69262306a36Sopenharmony_ci		break;
69362306a36Sopenharmony_ci	default:
69462306a36Sopenharmony_ci		ASD_DPRINTK("%s: phy%d: sub_func:0x%x?\n", __func__,
69562306a36Sopenharmony_ci			    phy_id, control_phy->sub_func);
69662306a36Sopenharmony_ci		break;
69762306a36Sopenharmony_ci	}
69862306a36Sopenharmony_ciout:
69962306a36Sopenharmony_ci	asd_ascb_free(ascb);
70062306a36Sopenharmony_ci}
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_cistatic void set_speed_mask(u8 *speed_mask, struct asd_phy_desc *pd)
70362306a36Sopenharmony_ci{
70462306a36Sopenharmony_ci	/* disable all speeds, then enable defaults */
70562306a36Sopenharmony_ci	*speed_mask = SAS_SPEED_60_DIS | SAS_SPEED_30_DIS | SAS_SPEED_15_DIS
70662306a36Sopenharmony_ci		| SATA_SPEED_30_DIS | SATA_SPEED_15_DIS;
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_ci	switch (pd->max_sas_lrate) {
70962306a36Sopenharmony_ci	case SAS_LINK_RATE_6_0_GBPS:
71062306a36Sopenharmony_ci		*speed_mask &= ~SAS_SPEED_60_DIS;
71162306a36Sopenharmony_ci		fallthrough;
71262306a36Sopenharmony_ci	default:
71362306a36Sopenharmony_ci	case SAS_LINK_RATE_3_0_GBPS:
71462306a36Sopenharmony_ci		*speed_mask &= ~SAS_SPEED_30_DIS;
71562306a36Sopenharmony_ci		fallthrough;
71662306a36Sopenharmony_ci	case SAS_LINK_RATE_1_5_GBPS:
71762306a36Sopenharmony_ci		*speed_mask &= ~SAS_SPEED_15_DIS;
71862306a36Sopenharmony_ci	}
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci	switch (pd->min_sas_lrate) {
72162306a36Sopenharmony_ci	case SAS_LINK_RATE_6_0_GBPS:
72262306a36Sopenharmony_ci		*speed_mask |= SAS_SPEED_30_DIS;
72362306a36Sopenharmony_ci		fallthrough;
72462306a36Sopenharmony_ci	case SAS_LINK_RATE_3_0_GBPS:
72562306a36Sopenharmony_ci		*speed_mask |= SAS_SPEED_15_DIS;
72662306a36Sopenharmony_ci		fallthrough;
72762306a36Sopenharmony_ci	default:
72862306a36Sopenharmony_ci	case SAS_LINK_RATE_1_5_GBPS:
72962306a36Sopenharmony_ci		/* nothing to do */
73062306a36Sopenharmony_ci		;
73162306a36Sopenharmony_ci	}
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_ci	switch (pd->max_sata_lrate) {
73462306a36Sopenharmony_ci	case SAS_LINK_RATE_3_0_GBPS:
73562306a36Sopenharmony_ci		*speed_mask &= ~SATA_SPEED_30_DIS;
73662306a36Sopenharmony_ci		fallthrough;
73762306a36Sopenharmony_ci	default:
73862306a36Sopenharmony_ci	case SAS_LINK_RATE_1_5_GBPS:
73962306a36Sopenharmony_ci		*speed_mask &= ~SATA_SPEED_15_DIS;
74062306a36Sopenharmony_ci	}
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci	switch (pd->min_sata_lrate) {
74362306a36Sopenharmony_ci	case SAS_LINK_RATE_3_0_GBPS:
74462306a36Sopenharmony_ci		*speed_mask |= SATA_SPEED_15_DIS;
74562306a36Sopenharmony_ci		fallthrough;
74662306a36Sopenharmony_ci	default:
74762306a36Sopenharmony_ci	case SAS_LINK_RATE_1_5_GBPS:
74862306a36Sopenharmony_ci		/* nothing to do */
74962306a36Sopenharmony_ci		;
75062306a36Sopenharmony_ci	}
75162306a36Sopenharmony_ci}
75262306a36Sopenharmony_ci
75362306a36Sopenharmony_ci/**
75462306a36Sopenharmony_ci * asd_build_control_phy -- build a CONTROL PHY SCB
75562306a36Sopenharmony_ci * @ascb: pointer to an ascb
75662306a36Sopenharmony_ci * @phy_id: phy id to control, integer
75762306a36Sopenharmony_ci * @subfunc: subfunction, what to actually to do the phy
75862306a36Sopenharmony_ci *
75962306a36Sopenharmony_ci * This function builds a CONTROL PHY scb.  No allocation of any kind
76062306a36Sopenharmony_ci * is performed. @ascb is allocated with the list function.
76162306a36Sopenharmony_ci * The caller can override the ascb->tasklet_complete to point
76262306a36Sopenharmony_ci * to its own callback function.  It must call asd_ascb_free()
76362306a36Sopenharmony_ci * at its tasklet complete function.
76462306a36Sopenharmony_ci * See the default implementation.
76562306a36Sopenharmony_ci */
76662306a36Sopenharmony_civoid asd_build_control_phy(struct asd_ascb *ascb, int phy_id, u8 subfunc)
76762306a36Sopenharmony_ci{
76862306a36Sopenharmony_ci	struct asd_phy *phy = &ascb->ha->phys[phy_id];
76962306a36Sopenharmony_ci	struct scb *scb = ascb->scb;
77062306a36Sopenharmony_ci	struct control_phy *control_phy = &scb->control_phy;
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_ci	scb->header.opcode = CONTROL_PHY;
77362306a36Sopenharmony_ci	control_phy->phy_id = (u8) phy_id;
77462306a36Sopenharmony_ci	control_phy->sub_func = subfunc;
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_ci	switch (subfunc) {
77762306a36Sopenharmony_ci	case EXECUTE_HARD_RESET:  /* 0x81 */
77862306a36Sopenharmony_ci	case ENABLE_PHY:          /* 0x01 */
77962306a36Sopenharmony_ci		/* decide hot plug delay */
78062306a36Sopenharmony_ci		control_phy->hot_plug_delay = HOTPLUG_DELAY_TIMEOUT;
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_ci		/* decide speed mask */
78362306a36Sopenharmony_ci		set_speed_mask(&control_phy->speed_mask, phy->phy_desc);
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_ci		/* initiator port settings are in the hi nibble */
78662306a36Sopenharmony_ci		if (phy->sas_phy.role == PHY_ROLE_INITIATOR)
78762306a36Sopenharmony_ci			control_phy->port_type = SAS_PROTOCOL_ALL << 4;
78862306a36Sopenharmony_ci		else if (phy->sas_phy.role == PHY_ROLE_TARGET)
78962306a36Sopenharmony_ci			control_phy->port_type = SAS_PROTOCOL_ALL;
79062306a36Sopenharmony_ci		else
79162306a36Sopenharmony_ci			control_phy->port_type =
79262306a36Sopenharmony_ci				(SAS_PROTOCOL_ALL << 4) | SAS_PROTOCOL_ALL;
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ci		/* link reset retries, this should be nominal */
79562306a36Sopenharmony_ci		control_phy->link_reset_retries = 10;
79662306a36Sopenharmony_ci		fallthrough;
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_ci	case RELEASE_SPINUP_HOLD: /* 0x02 */
79962306a36Sopenharmony_ci		/* decide the func_mask */
80062306a36Sopenharmony_ci		control_phy->func_mask = FUNCTION_MASK_DEFAULT;
80162306a36Sopenharmony_ci		if (phy->phy_desc->flags & ASD_SATA_SPINUP_HOLD)
80262306a36Sopenharmony_ci			control_phy->func_mask &= ~SPINUP_HOLD_DIS;
80362306a36Sopenharmony_ci		else
80462306a36Sopenharmony_ci			control_phy->func_mask |= SPINUP_HOLD_DIS;
80562306a36Sopenharmony_ci	}
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_ci	control_phy->conn_handle = cpu_to_le16(0xFFFF);
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_ci	ascb->tasklet_complete = control_phy_tasklet_complete;
81062306a36Sopenharmony_ci}
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_ci/* ---------- INITIATE LINK ADM TASK ---------- */
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_ci#if 0
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_cistatic void link_adm_tasklet_complete(struct asd_ascb *ascb,
81762306a36Sopenharmony_ci				      struct done_list_struct *dl)
81862306a36Sopenharmony_ci{
81962306a36Sopenharmony_ci	u8 opcode = dl->opcode;
82062306a36Sopenharmony_ci	struct initiate_link_adm *link_adm = &ascb->scb->link_adm;
82162306a36Sopenharmony_ci	u8 phy_id = link_adm->phy_id;
82262306a36Sopenharmony_ci
82362306a36Sopenharmony_ci	if (opcode != TC_NO_ERROR) {
82462306a36Sopenharmony_ci		asd_printk("phy%d: link adm task 0x%x completed with error "
82562306a36Sopenharmony_ci			   "0x%x\n", phy_id, link_adm->sub_func, opcode);
82662306a36Sopenharmony_ci	}
82762306a36Sopenharmony_ci	ASD_DPRINTK("phy%d: link adm task 0x%x: 0x%x\n",
82862306a36Sopenharmony_ci		    phy_id, link_adm->sub_func, opcode);
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_ci	asd_ascb_free(ascb);
83162306a36Sopenharmony_ci}
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_civoid asd_build_initiate_link_adm_task(struct asd_ascb *ascb, int phy_id,
83462306a36Sopenharmony_ci				      u8 subfunc)
83562306a36Sopenharmony_ci{
83662306a36Sopenharmony_ci	struct scb *scb = ascb->scb;
83762306a36Sopenharmony_ci	struct initiate_link_adm *link_adm = &scb->link_adm;
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_ci	scb->header.opcode = INITIATE_LINK_ADM_TASK;
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_ci	link_adm->phy_id = phy_id;
84262306a36Sopenharmony_ci	link_adm->sub_func = subfunc;
84362306a36Sopenharmony_ci	link_adm->conn_handle = cpu_to_le16(0xFFFF);
84462306a36Sopenharmony_ci
84562306a36Sopenharmony_ci	ascb->tasklet_complete = link_adm_tasklet_complete;
84662306a36Sopenharmony_ci}
84762306a36Sopenharmony_ci
84862306a36Sopenharmony_ci#endif  /*  0  */
84962306a36Sopenharmony_ci
85062306a36Sopenharmony_ci/* ---------- SCB timer ---------- */
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_ci/**
85362306a36Sopenharmony_ci * asd_ascb_timedout -- called when a pending SCB's timer has expired
85462306a36Sopenharmony_ci * @t: Timer context used to fetch the SCB
85562306a36Sopenharmony_ci *
85662306a36Sopenharmony_ci * This is the default timeout function which does the most necessary.
85762306a36Sopenharmony_ci * Upper layers can implement their own timeout function, say to free
85862306a36Sopenharmony_ci * resources they have with this SCB, and then call this one at the
85962306a36Sopenharmony_ci * end of their timeout function.  To do this, one should initialize
86062306a36Sopenharmony_ci * the ascb->timer.{function, expires} prior to calling the post
86162306a36Sopenharmony_ci * function. The timer is started by the post function.
86262306a36Sopenharmony_ci */
86362306a36Sopenharmony_civoid asd_ascb_timedout(struct timer_list *t)
86462306a36Sopenharmony_ci{
86562306a36Sopenharmony_ci	struct asd_ascb *ascb = from_timer(ascb, t, timer);
86662306a36Sopenharmony_ci	struct asd_seq_data *seq = &ascb->ha->seq;
86762306a36Sopenharmony_ci	unsigned long flags;
86862306a36Sopenharmony_ci
86962306a36Sopenharmony_ci	ASD_DPRINTK("scb:0x%x timed out\n", ascb->scb->header.opcode);
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_ci	spin_lock_irqsave(&seq->pend_q_lock, flags);
87262306a36Sopenharmony_ci	seq->pending--;
87362306a36Sopenharmony_ci	list_del_init(&ascb->list);
87462306a36Sopenharmony_ci	spin_unlock_irqrestore(&seq->pend_q_lock, flags);
87562306a36Sopenharmony_ci
87662306a36Sopenharmony_ci	asd_ascb_free(ascb);
87762306a36Sopenharmony_ci}
87862306a36Sopenharmony_ci
87962306a36Sopenharmony_ci/* ---------- CONTROL PHY ---------- */
88062306a36Sopenharmony_ci
88162306a36Sopenharmony_ci/* Given the spec value, return a driver value. */
88262306a36Sopenharmony_cistatic const int phy_func_table[] = {
88362306a36Sopenharmony_ci	[PHY_FUNC_NOP]        = PHY_NO_OP,
88462306a36Sopenharmony_ci	[PHY_FUNC_LINK_RESET] = ENABLE_PHY,
88562306a36Sopenharmony_ci	[PHY_FUNC_HARD_RESET] = EXECUTE_HARD_RESET,
88662306a36Sopenharmony_ci	[PHY_FUNC_DISABLE]    = DISABLE_PHY,
88762306a36Sopenharmony_ci	[PHY_FUNC_RELEASE_SPINUP_HOLD] = RELEASE_SPINUP_HOLD,
88862306a36Sopenharmony_ci};
88962306a36Sopenharmony_ci
89062306a36Sopenharmony_ciint asd_control_phy(struct asd_sas_phy *phy, enum phy_func func, void *arg)
89162306a36Sopenharmony_ci{
89262306a36Sopenharmony_ci	struct asd_ha_struct *asd_ha = phy->ha->lldd_ha;
89362306a36Sopenharmony_ci	struct asd_phy_desc *pd = asd_ha->phys[phy->id].phy_desc;
89462306a36Sopenharmony_ci	struct asd_ascb *ascb;
89562306a36Sopenharmony_ci	struct sas_phy_linkrates *rates;
89662306a36Sopenharmony_ci	int res = 1;
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_ci	switch (func) {
89962306a36Sopenharmony_ci	case PHY_FUNC_CLEAR_ERROR_LOG:
90062306a36Sopenharmony_ci	case PHY_FUNC_GET_EVENTS:
90162306a36Sopenharmony_ci		return -ENOSYS;
90262306a36Sopenharmony_ci	case PHY_FUNC_SET_LINK_RATE:
90362306a36Sopenharmony_ci		rates = arg;
90462306a36Sopenharmony_ci		if (rates->minimum_linkrate) {
90562306a36Sopenharmony_ci			pd->min_sas_lrate = rates->minimum_linkrate;
90662306a36Sopenharmony_ci			pd->min_sata_lrate = rates->minimum_linkrate;
90762306a36Sopenharmony_ci		}
90862306a36Sopenharmony_ci		if (rates->maximum_linkrate) {
90962306a36Sopenharmony_ci			pd->max_sas_lrate = rates->maximum_linkrate;
91062306a36Sopenharmony_ci			pd->max_sata_lrate = rates->maximum_linkrate;
91162306a36Sopenharmony_ci		}
91262306a36Sopenharmony_ci		func = PHY_FUNC_LINK_RESET;
91362306a36Sopenharmony_ci		break;
91462306a36Sopenharmony_ci	default:
91562306a36Sopenharmony_ci		break;
91662306a36Sopenharmony_ci	}
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_ci	ascb = asd_ascb_alloc_list(asd_ha, &res, GFP_KERNEL);
91962306a36Sopenharmony_ci	if (!ascb)
92062306a36Sopenharmony_ci		return -ENOMEM;
92162306a36Sopenharmony_ci
92262306a36Sopenharmony_ci	asd_build_control_phy(ascb, phy->id, phy_func_table[func]);
92362306a36Sopenharmony_ci	res = asd_post_ascb_list(asd_ha, ascb , 1);
92462306a36Sopenharmony_ci	if (res)
92562306a36Sopenharmony_ci		asd_ascb_free(ascb);
92662306a36Sopenharmony_ci
92762306a36Sopenharmony_ci	return res;
92862306a36Sopenharmony_ci}
929