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