162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Aic94xx SAS/SATA driver hardware interface. 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/pci.h> 1062306a36Sopenharmony_ci#include <linux/slab.h> 1162306a36Sopenharmony_ci#include <linux/delay.h> 1262306a36Sopenharmony_ci#include <linux/module.h> 1362306a36Sopenharmony_ci#include <linux/firmware.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include "aic94xx.h" 1662306a36Sopenharmony_ci#include "aic94xx_reg.h" 1762306a36Sopenharmony_ci#include "aic94xx_hwi.h" 1862306a36Sopenharmony_ci#include "aic94xx_seq.h" 1962306a36Sopenharmony_ci#include "aic94xx_dump.h" 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ciu32 MBAR0_SWB_SIZE; 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci/* ---------- Initialization ---------- */ 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_cistatic int asd_get_user_sas_addr(struct asd_ha_struct *asd_ha) 2662306a36Sopenharmony_ci{ 2762306a36Sopenharmony_ci /* adapter came with a sas address */ 2862306a36Sopenharmony_ci if (asd_ha->hw_prof.sas_addr[0]) 2962306a36Sopenharmony_ci return 0; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci return sas_request_addr(asd_ha->sas_ha.shost, 3262306a36Sopenharmony_ci asd_ha->hw_prof.sas_addr); 3362306a36Sopenharmony_ci} 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistatic void asd_propagate_sas_addr(struct asd_ha_struct *asd_ha) 3662306a36Sopenharmony_ci{ 3762306a36Sopenharmony_ci int i; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci for (i = 0; i < ASD_MAX_PHYS; i++) { 4062306a36Sopenharmony_ci if (asd_ha->hw_prof.phy_desc[i].sas_addr[0] == 0) 4162306a36Sopenharmony_ci continue; 4262306a36Sopenharmony_ci /* Set a phy's address only if it has none. 4362306a36Sopenharmony_ci */ 4462306a36Sopenharmony_ci ASD_DPRINTK("setting phy%d addr to %llx\n", i, 4562306a36Sopenharmony_ci SAS_ADDR(asd_ha->hw_prof.sas_addr)); 4662306a36Sopenharmony_ci memcpy(asd_ha->hw_prof.phy_desc[i].sas_addr, 4762306a36Sopenharmony_ci asd_ha->hw_prof.sas_addr, SAS_ADDR_SIZE); 4862306a36Sopenharmony_ci } 4962306a36Sopenharmony_ci} 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci/* ---------- PHY initialization ---------- */ 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cistatic void asd_init_phy_identify(struct asd_phy *phy) 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci phy->identify_frame = phy->id_frm_tok->vaddr; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci memset(phy->identify_frame, 0, sizeof(*phy->identify_frame)); 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci phy->identify_frame->dev_type = SAS_END_DEVICE; 6062306a36Sopenharmony_ci if (phy->sas_phy.role & PHY_ROLE_INITIATOR) 6162306a36Sopenharmony_ci phy->identify_frame->initiator_bits = phy->sas_phy.iproto; 6262306a36Sopenharmony_ci if (phy->sas_phy.role & PHY_ROLE_TARGET) 6362306a36Sopenharmony_ci phy->identify_frame->target_bits = phy->sas_phy.tproto; 6462306a36Sopenharmony_ci memcpy(phy->identify_frame->sas_addr, phy->phy_desc->sas_addr, 6562306a36Sopenharmony_ci SAS_ADDR_SIZE); 6662306a36Sopenharmony_ci phy->identify_frame->phy_id = phy->sas_phy.id; 6762306a36Sopenharmony_ci} 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_cistatic int asd_init_phy(struct asd_phy *phy) 7062306a36Sopenharmony_ci{ 7162306a36Sopenharmony_ci struct asd_ha_struct *asd_ha = phy->sas_phy.ha->lldd_ha; 7262306a36Sopenharmony_ci struct asd_sas_phy *sas_phy = &phy->sas_phy; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci sas_phy->enabled = 1; 7562306a36Sopenharmony_ci sas_phy->iproto = SAS_PROTOCOL_ALL; 7662306a36Sopenharmony_ci sas_phy->tproto = 0; 7762306a36Sopenharmony_ci sas_phy->role = PHY_ROLE_INITIATOR; 7862306a36Sopenharmony_ci sas_phy->oob_mode = OOB_NOT_CONNECTED; 7962306a36Sopenharmony_ci sas_phy->linkrate = SAS_LINK_RATE_UNKNOWN; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci phy->id_frm_tok = asd_alloc_coherent(asd_ha, 8262306a36Sopenharmony_ci sizeof(*phy->identify_frame), 8362306a36Sopenharmony_ci GFP_KERNEL); 8462306a36Sopenharmony_ci if (!phy->id_frm_tok) { 8562306a36Sopenharmony_ci asd_printk("no mem for IDENTIFY for phy%d\n", sas_phy->id); 8662306a36Sopenharmony_ci return -ENOMEM; 8762306a36Sopenharmony_ci } else 8862306a36Sopenharmony_ci asd_init_phy_identify(phy); 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci memset(phy->frame_rcvd, 0, sizeof(phy->frame_rcvd)); 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci return 0; 9362306a36Sopenharmony_ci} 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_cistatic void asd_init_ports(struct asd_ha_struct *asd_ha) 9662306a36Sopenharmony_ci{ 9762306a36Sopenharmony_ci int i; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci spin_lock_init(&asd_ha->asd_ports_lock); 10062306a36Sopenharmony_ci for (i = 0; i < ASD_MAX_PHYS; i++) { 10162306a36Sopenharmony_ci struct asd_port *asd_port = &asd_ha->asd_ports[i]; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci memset(asd_port->sas_addr, 0, SAS_ADDR_SIZE); 10462306a36Sopenharmony_ci memset(asd_port->attached_sas_addr, 0, SAS_ADDR_SIZE); 10562306a36Sopenharmony_ci asd_port->phy_mask = 0; 10662306a36Sopenharmony_ci asd_port->num_phys = 0; 10762306a36Sopenharmony_ci } 10862306a36Sopenharmony_ci} 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_cistatic int asd_init_phys(struct asd_ha_struct *asd_ha) 11162306a36Sopenharmony_ci{ 11262306a36Sopenharmony_ci u8 i; 11362306a36Sopenharmony_ci u8 phy_mask = asd_ha->hw_prof.enabled_phys; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci for (i = 0; i < ASD_MAX_PHYS; i++) { 11662306a36Sopenharmony_ci struct asd_phy *phy = &asd_ha->phys[i]; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci phy->phy_desc = &asd_ha->hw_prof.phy_desc[i]; 11962306a36Sopenharmony_ci phy->asd_port = NULL; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci phy->sas_phy.enabled = 0; 12262306a36Sopenharmony_ci phy->sas_phy.id = i; 12362306a36Sopenharmony_ci phy->sas_phy.sas_addr = &phy->phy_desc->sas_addr[0]; 12462306a36Sopenharmony_ci phy->sas_phy.frame_rcvd = &phy->frame_rcvd[0]; 12562306a36Sopenharmony_ci phy->sas_phy.ha = &asd_ha->sas_ha; 12662306a36Sopenharmony_ci phy->sas_phy.lldd_phy = phy; 12762306a36Sopenharmony_ci } 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci /* Now enable and initialize only the enabled phys. */ 13062306a36Sopenharmony_ci for_each_phy(phy_mask, phy_mask, i) { 13162306a36Sopenharmony_ci int err = asd_init_phy(&asd_ha->phys[i]); 13262306a36Sopenharmony_ci if (err) 13362306a36Sopenharmony_ci return err; 13462306a36Sopenharmony_ci } 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci return 0; 13762306a36Sopenharmony_ci} 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci/* ---------- Sliding windows ---------- */ 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_cistatic int asd_init_sw(struct asd_ha_struct *asd_ha) 14262306a36Sopenharmony_ci{ 14362306a36Sopenharmony_ci struct pci_dev *pcidev = asd_ha->pcidev; 14462306a36Sopenharmony_ci int err; 14562306a36Sopenharmony_ci u32 v; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci /* Unlock MBARs */ 14862306a36Sopenharmony_ci err = pci_read_config_dword(pcidev, PCI_CONF_MBAR_KEY, &v); 14962306a36Sopenharmony_ci if (err) { 15062306a36Sopenharmony_ci asd_printk("couldn't access conf. space of %s\n", 15162306a36Sopenharmony_ci pci_name(pcidev)); 15262306a36Sopenharmony_ci goto Err; 15362306a36Sopenharmony_ci } 15462306a36Sopenharmony_ci if (v) 15562306a36Sopenharmony_ci err = pci_write_config_dword(pcidev, PCI_CONF_MBAR_KEY, v); 15662306a36Sopenharmony_ci if (err) { 15762306a36Sopenharmony_ci asd_printk("couldn't write to MBAR_KEY of %s\n", 15862306a36Sopenharmony_ci pci_name(pcidev)); 15962306a36Sopenharmony_ci goto Err; 16062306a36Sopenharmony_ci } 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci /* Set sliding windows A, B and C to point to proper internal 16362306a36Sopenharmony_ci * memory regions. 16462306a36Sopenharmony_ci */ 16562306a36Sopenharmony_ci pci_write_config_dword(pcidev, PCI_CONF_MBAR0_SWA, REG_BASE_ADDR); 16662306a36Sopenharmony_ci pci_write_config_dword(pcidev, PCI_CONF_MBAR0_SWB, 16762306a36Sopenharmony_ci REG_BASE_ADDR_CSEQCIO); 16862306a36Sopenharmony_ci pci_write_config_dword(pcidev, PCI_CONF_MBAR0_SWC, REG_BASE_ADDR_EXSI); 16962306a36Sopenharmony_ci asd_ha->io_handle[0].swa_base = REG_BASE_ADDR; 17062306a36Sopenharmony_ci asd_ha->io_handle[0].swb_base = REG_BASE_ADDR_CSEQCIO; 17162306a36Sopenharmony_ci asd_ha->io_handle[0].swc_base = REG_BASE_ADDR_EXSI; 17262306a36Sopenharmony_ci MBAR0_SWB_SIZE = asd_ha->io_handle[0].len - 0x80; 17362306a36Sopenharmony_ci if (!asd_ha->iospace) { 17462306a36Sopenharmony_ci /* MBAR1 will point to OCM (On Chip Memory) */ 17562306a36Sopenharmony_ci pci_write_config_dword(pcidev, PCI_CONF_MBAR1, OCM_BASE_ADDR); 17662306a36Sopenharmony_ci asd_ha->io_handle[1].swa_base = OCM_BASE_ADDR; 17762306a36Sopenharmony_ci } 17862306a36Sopenharmony_ci spin_lock_init(&asd_ha->iolock); 17962306a36Sopenharmony_ciErr: 18062306a36Sopenharmony_ci return err; 18162306a36Sopenharmony_ci} 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci/* ---------- SCB initialization ---------- */ 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci/** 18662306a36Sopenharmony_ci * asd_init_scbs - manually allocate the first SCB. 18762306a36Sopenharmony_ci * @asd_ha: pointer to host adapter structure 18862306a36Sopenharmony_ci * 18962306a36Sopenharmony_ci * This allocates the very first SCB which would be sent to the 19062306a36Sopenharmony_ci * sequencer for execution. Its bus address is written to 19162306a36Sopenharmony_ci * CSEQ_Q_NEW_POINTER, mode page 2, mode 8. Since the bus address of 19262306a36Sopenharmony_ci * the _next_ scb to be DMA-ed to the host adapter is read from the last 19362306a36Sopenharmony_ci * SCB DMA-ed to the host adapter, we have to always stay one step 19462306a36Sopenharmony_ci * ahead of the sequencer and keep one SCB already allocated. 19562306a36Sopenharmony_ci */ 19662306a36Sopenharmony_cistatic int asd_init_scbs(struct asd_ha_struct *asd_ha) 19762306a36Sopenharmony_ci{ 19862306a36Sopenharmony_ci struct asd_seq_data *seq = &asd_ha->seq; 19962306a36Sopenharmony_ci int bitmap_bytes; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci /* allocate the index array and bitmap */ 20262306a36Sopenharmony_ci asd_ha->seq.tc_index_bitmap_bits = asd_ha->hw_prof.max_scbs; 20362306a36Sopenharmony_ci asd_ha->seq.tc_index_array = kcalloc(asd_ha->seq.tc_index_bitmap_bits, 20462306a36Sopenharmony_ci sizeof(void *), 20562306a36Sopenharmony_ci GFP_KERNEL); 20662306a36Sopenharmony_ci if (!asd_ha->seq.tc_index_array) 20762306a36Sopenharmony_ci return -ENOMEM; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci bitmap_bytes = (asd_ha->seq.tc_index_bitmap_bits+7)/8; 21062306a36Sopenharmony_ci bitmap_bytes = BITS_TO_LONGS(bitmap_bytes*8)*sizeof(unsigned long); 21162306a36Sopenharmony_ci asd_ha->seq.tc_index_bitmap = kzalloc(bitmap_bytes, GFP_KERNEL); 21262306a36Sopenharmony_ci if (!asd_ha->seq.tc_index_bitmap) { 21362306a36Sopenharmony_ci kfree(asd_ha->seq.tc_index_array); 21462306a36Sopenharmony_ci asd_ha->seq.tc_index_array = NULL; 21562306a36Sopenharmony_ci return -ENOMEM; 21662306a36Sopenharmony_ci } 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci spin_lock_init(&seq->tc_index_lock); 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci seq->next_scb.size = sizeof(struct scb); 22162306a36Sopenharmony_ci seq->next_scb.vaddr = dma_pool_alloc(asd_ha->scb_pool, GFP_KERNEL, 22262306a36Sopenharmony_ci &seq->next_scb.dma_handle); 22362306a36Sopenharmony_ci if (!seq->next_scb.vaddr) { 22462306a36Sopenharmony_ci kfree(asd_ha->seq.tc_index_bitmap); 22562306a36Sopenharmony_ci kfree(asd_ha->seq.tc_index_array); 22662306a36Sopenharmony_ci asd_ha->seq.tc_index_bitmap = NULL; 22762306a36Sopenharmony_ci asd_ha->seq.tc_index_array = NULL; 22862306a36Sopenharmony_ci return -ENOMEM; 22962306a36Sopenharmony_ci } 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci seq->pending = 0; 23262306a36Sopenharmony_ci spin_lock_init(&seq->pend_q_lock); 23362306a36Sopenharmony_ci INIT_LIST_HEAD(&seq->pend_q); 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci return 0; 23662306a36Sopenharmony_ci} 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_cistatic void asd_get_max_scb_ddb(struct asd_ha_struct *asd_ha) 23962306a36Sopenharmony_ci{ 24062306a36Sopenharmony_ci asd_ha->hw_prof.max_scbs = asd_get_cmdctx_size(asd_ha)/ASD_SCB_SIZE; 24162306a36Sopenharmony_ci asd_ha->hw_prof.max_ddbs = asd_get_devctx_size(asd_ha)/ASD_DDB_SIZE; 24262306a36Sopenharmony_ci ASD_DPRINTK("max_scbs:%d, max_ddbs:%d\n", 24362306a36Sopenharmony_ci asd_ha->hw_prof.max_scbs, 24462306a36Sopenharmony_ci asd_ha->hw_prof.max_ddbs); 24562306a36Sopenharmony_ci} 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci/* ---------- Done List initialization ---------- */ 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_cistatic void asd_dl_tasklet_handler(unsigned long); 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_cistatic int asd_init_dl(struct asd_ha_struct *asd_ha) 25262306a36Sopenharmony_ci{ 25362306a36Sopenharmony_ci asd_ha->seq.actual_dl 25462306a36Sopenharmony_ci = asd_alloc_coherent(asd_ha, 25562306a36Sopenharmony_ci ASD_DL_SIZE * sizeof(struct done_list_struct), 25662306a36Sopenharmony_ci GFP_KERNEL); 25762306a36Sopenharmony_ci if (!asd_ha->seq.actual_dl) 25862306a36Sopenharmony_ci return -ENOMEM; 25962306a36Sopenharmony_ci asd_ha->seq.dl = asd_ha->seq.actual_dl->vaddr; 26062306a36Sopenharmony_ci asd_ha->seq.dl_toggle = ASD_DEF_DL_TOGGLE; 26162306a36Sopenharmony_ci asd_ha->seq.dl_next = 0; 26262306a36Sopenharmony_ci tasklet_init(&asd_ha->seq.dl_tasklet, asd_dl_tasklet_handler, 26362306a36Sopenharmony_ci (unsigned long) asd_ha); 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci return 0; 26662306a36Sopenharmony_ci} 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci/* ---------- EDB and ESCB init ---------- */ 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_cistatic int asd_alloc_edbs(struct asd_ha_struct *asd_ha, gfp_t gfp_flags) 27162306a36Sopenharmony_ci{ 27262306a36Sopenharmony_ci struct asd_seq_data *seq = &asd_ha->seq; 27362306a36Sopenharmony_ci int i; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci seq->edb_arr = kmalloc_array(seq->num_edbs, sizeof(*seq->edb_arr), 27662306a36Sopenharmony_ci gfp_flags); 27762306a36Sopenharmony_ci if (!seq->edb_arr) 27862306a36Sopenharmony_ci return -ENOMEM; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci for (i = 0; i < seq->num_edbs; i++) { 28162306a36Sopenharmony_ci seq->edb_arr[i] = asd_alloc_coherent(asd_ha, ASD_EDB_SIZE, 28262306a36Sopenharmony_ci gfp_flags); 28362306a36Sopenharmony_ci if (!seq->edb_arr[i]) 28462306a36Sopenharmony_ci goto Err_unroll; 28562306a36Sopenharmony_ci memset(seq->edb_arr[i]->vaddr, 0, ASD_EDB_SIZE); 28662306a36Sopenharmony_ci } 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci ASD_DPRINTK("num_edbs:%d\n", seq->num_edbs); 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci return 0; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ciErr_unroll: 29362306a36Sopenharmony_ci for (i-- ; i >= 0; i--) 29462306a36Sopenharmony_ci asd_free_coherent(asd_ha, seq->edb_arr[i]); 29562306a36Sopenharmony_ci kfree(seq->edb_arr); 29662306a36Sopenharmony_ci seq->edb_arr = NULL; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci return -ENOMEM; 29962306a36Sopenharmony_ci} 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_cistatic int asd_alloc_escbs(struct asd_ha_struct *asd_ha, 30262306a36Sopenharmony_ci gfp_t gfp_flags) 30362306a36Sopenharmony_ci{ 30462306a36Sopenharmony_ci struct asd_seq_data *seq = &asd_ha->seq; 30562306a36Sopenharmony_ci struct asd_ascb *escb; 30662306a36Sopenharmony_ci int i, escbs; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci seq->escb_arr = kmalloc_array(seq->num_escbs, sizeof(*seq->escb_arr), 30962306a36Sopenharmony_ci gfp_flags); 31062306a36Sopenharmony_ci if (!seq->escb_arr) 31162306a36Sopenharmony_ci return -ENOMEM; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci escbs = seq->num_escbs; 31462306a36Sopenharmony_ci escb = asd_ascb_alloc_list(asd_ha, &escbs, gfp_flags); 31562306a36Sopenharmony_ci if (!escb) { 31662306a36Sopenharmony_ci asd_printk("couldn't allocate list of escbs\n"); 31762306a36Sopenharmony_ci goto Err; 31862306a36Sopenharmony_ci } 31962306a36Sopenharmony_ci seq->num_escbs -= escbs; /* subtract what was not allocated */ 32062306a36Sopenharmony_ci ASD_DPRINTK("num_escbs:%d\n", seq->num_escbs); 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci for (i = 0; i < seq->num_escbs; i++, escb = list_entry(escb->list.next, 32362306a36Sopenharmony_ci struct asd_ascb, 32462306a36Sopenharmony_ci list)) { 32562306a36Sopenharmony_ci seq->escb_arr[i] = escb; 32662306a36Sopenharmony_ci escb->scb->header.opcode = EMPTY_SCB; 32762306a36Sopenharmony_ci } 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci return 0; 33062306a36Sopenharmony_ciErr: 33162306a36Sopenharmony_ci kfree(seq->escb_arr); 33262306a36Sopenharmony_ci seq->escb_arr = NULL; 33362306a36Sopenharmony_ci return -ENOMEM; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci} 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_cistatic void asd_assign_edbs2escbs(struct asd_ha_struct *asd_ha) 33862306a36Sopenharmony_ci{ 33962306a36Sopenharmony_ci struct asd_seq_data *seq = &asd_ha->seq; 34062306a36Sopenharmony_ci int i, k, z = 0; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci for (i = 0; i < seq->num_escbs; i++) { 34362306a36Sopenharmony_ci struct asd_ascb *ascb = seq->escb_arr[i]; 34462306a36Sopenharmony_ci struct empty_scb *escb = &ascb->scb->escb; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci ascb->edb_index = z; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci escb->num_valid = ASD_EDBS_PER_SCB; 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci for (k = 0; k < ASD_EDBS_PER_SCB; k++) { 35162306a36Sopenharmony_ci struct sg_el *eb = &escb->eb[k]; 35262306a36Sopenharmony_ci struct asd_dma_tok *edb = seq->edb_arr[z++]; 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci memset(eb, 0, sizeof(*eb)); 35562306a36Sopenharmony_ci eb->bus_addr = cpu_to_le64(((u64) edb->dma_handle)); 35662306a36Sopenharmony_ci eb->size = cpu_to_le32(((u32) edb->size)); 35762306a36Sopenharmony_ci } 35862306a36Sopenharmony_ci } 35962306a36Sopenharmony_ci} 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci/** 36262306a36Sopenharmony_ci * asd_init_escbs -- allocate and initialize empty scbs 36362306a36Sopenharmony_ci * @asd_ha: pointer to host adapter structure 36462306a36Sopenharmony_ci * 36562306a36Sopenharmony_ci * An empty SCB has sg_elements of ASD_EDBS_PER_SCB (7) buffers. 36662306a36Sopenharmony_ci * They transport sense data, etc. 36762306a36Sopenharmony_ci */ 36862306a36Sopenharmony_cistatic int asd_init_escbs(struct asd_ha_struct *asd_ha) 36962306a36Sopenharmony_ci{ 37062306a36Sopenharmony_ci struct asd_seq_data *seq = &asd_ha->seq; 37162306a36Sopenharmony_ci int err = 0; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci /* Allocate two empty data buffers (edb) per sequencer. */ 37462306a36Sopenharmony_ci int edbs = 2*(1+asd_ha->hw_prof.num_phys); 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci seq->num_escbs = (edbs+ASD_EDBS_PER_SCB-1)/ASD_EDBS_PER_SCB; 37762306a36Sopenharmony_ci seq->num_edbs = seq->num_escbs * ASD_EDBS_PER_SCB; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci err = asd_alloc_edbs(asd_ha, GFP_KERNEL); 38062306a36Sopenharmony_ci if (err) { 38162306a36Sopenharmony_ci asd_printk("couldn't allocate edbs\n"); 38262306a36Sopenharmony_ci return err; 38362306a36Sopenharmony_ci } 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci err = asd_alloc_escbs(asd_ha, GFP_KERNEL); 38662306a36Sopenharmony_ci if (err) { 38762306a36Sopenharmony_ci asd_printk("couldn't allocate escbs\n"); 38862306a36Sopenharmony_ci return err; 38962306a36Sopenharmony_ci } 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci asd_assign_edbs2escbs(asd_ha); 39262306a36Sopenharmony_ci /* In order to insure that normal SCBs do not overfill sequencer 39362306a36Sopenharmony_ci * memory and leave no space for escbs (halting condition), 39462306a36Sopenharmony_ci * we increment pending here by the number of escbs. However, 39562306a36Sopenharmony_ci * escbs are never pending. 39662306a36Sopenharmony_ci */ 39762306a36Sopenharmony_ci seq->pending = seq->num_escbs; 39862306a36Sopenharmony_ci seq->can_queue = 1 + (asd_ha->hw_prof.max_scbs - seq->pending)/2; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci return 0; 40162306a36Sopenharmony_ci} 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci/* ---------- HW initialization ---------- */ 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci/** 40662306a36Sopenharmony_ci * asd_chip_hardrst -- hard reset the chip 40762306a36Sopenharmony_ci * @asd_ha: pointer to host adapter structure 40862306a36Sopenharmony_ci * 40962306a36Sopenharmony_ci * This takes 16 cycles and is synchronous to CFCLK, which runs 41062306a36Sopenharmony_ci * at 200 MHz, so this should take at most 80 nanoseconds. 41162306a36Sopenharmony_ci */ 41262306a36Sopenharmony_ciint asd_chip_hardrst(struct asd_ha_struct *asd_ha) 41362306a36Sopenharmony_ci{ 41462306a36Sopenharmony_ci int i; 41562306a36Sopenharmony_ci int count = 100; 41662306a36Sopenharmony_ci u32 reg; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci for (i = 0 ; i < 4 ; i++) { 41962306a36Sopenharmony_ci asd_write_reg_dword(asd_ha, COMBIST, HARDRST); 42062306a36Sopenharmony_ci } 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci do { 42362306a36Sopenharmony_ci udelay(1); 42462306a36Sopenharmony_ci reg = asd_read_reg_dword(asd_ha, CHIMINT); 42562306a36Sopenharmony_ci if (reg & HARDRSTDET) { 42662306a36Sopenharmony_ci asd_write_reg_dword(asd_ha, CHIMINT, 42762306a36Sopenharmony_ci HARDRSTDET|PORRSTDET); 42862306a36Sopenharmony_ci return 0; 42962306a36Sopenharmony_ci } 43062306a36Sopenharmony_ci } while (--count > 0); 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci return -ENODEV; 43362306a36Sopenharmony_ci} 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci/** 43662306a36Sopenharmony_ci * asd_init_chip -- initialize the chip 43762306a36Sopenharmony_ci * @asd_ha: pointer to host adapter structure 43862306a36Sopenharmony_ci * 43962306a36Sopenharmony_ci * Hard resets the chip, disables HA interrupts, downloads the sequnecer 44062306a36Sopenharmony_ci * microcode and starts the sequencers. The caller has to explicitly 44162306a36Sopenharmony_ci * enable HA interrupts with asd_enable_ints(asd_ha). 44262306a36Sopenharmony_ci */ 44362306a36Sopenharmony_cistatic int asd_init_chip(struct asd_ha_struct *asd_ha) 44462306a36Sopenharmony_ci{ 44562306a36Sopenharmony_ci int err; 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci err = asd_chip_hardrst(asd_ha); 44862306a36Sopenharmony_ci if (err) { 44962306a36Sopenharmony_ci asd_printk("couldn't hard reset %s\n", 45062306a36Sopenharmony_ci pci_name(asd_ha->pcidev)); 45162306a36Sopenharmony_ci goto out; 45262306a36Sopenharmony_ci } 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci asd_disable_ints(asd_ha); 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci err = asd_init_seqs(asd_ha); 45762306a36Sopenharmony_ci if (err) { 45862306a36Sopenharmony_ci asd_printk("couldn't init seqs for %s\n", 45962306a36Sopenharmony_ci pci_name(asd_ha->pcidev)); 46062306a36Sopenharmony_ci goto out; 46162306a36Sopenharmony_ci } 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci err = asd_start_seqs(asd_ha); 46462306a36Sopenharmony_ci if (err) { 46562306a36Sopenharmony_ci asd_printk("couldn't start seqs for %s\n", 46662306a36Sopenharmony_ci pci_name(asd_ha->pcidev)); 46762306a36Sopenharmony_ci goto out; 46862306a36Sopenharmony_ci } 46962306a36Sopenharmony_ciout: 47062306a36Sopenharmony_ci return err; 47162306a36Sopenharmony_ci} 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci#define MAX_DEVS ((OCM_MAX_SIZE) / (ASD_DDB_SIZE)) 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_cistatic int max_devs = 0; 47662306a36Sopenharmony_cimodule_param_named(max_devs, max_devs, int, S_IRUGO); 47762306a36Sopenharmony_ciMODULE_PARM_DESC(max_devs, "\n" 47862306a36Sopenharmony_ci "\tMaximum number of SAS devices to support (not LUs).\n" 47962306a36Sopenharmony_ci "\tDefault: 2176, Maximum: 65663.\n"); 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_cistatic int max_cmnds = 0; 48262306a36Sopenharmony_cimodule_param_named(max_cmnds, max_cmnds, int, S_IRUGO); 48362306a36Sopenharmony_ciMODULE_PARM_DESC(max_cmnds, "\n" 48462306a36Sopenharmony_ci "\tMaximum number of commands queuable.\n" 48562306a36Sopenharmony_ci "\tDefault: 512, Maximum: 66047.\n"); 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_cistatic void asd_extend_devctx_ocm(struct asd_ha_struct *asd_ha) 48862306a36Sopenharmony_ci{ 48962306a36Sopenharmony_ci unsigned long dma_addr = OCM_BASE_ADDR; 49062306a36Sopenharmony_ci u32 d; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci dma_addr -= asd_ha->hw_prof.max_ddbs * ASD_DDB_SIZE; 49362306a36Sopenharmony_ci asd_write_reg_addr(asd_ha, DEVCTXBASE, (dma_addr_t) dma_addr); 49462306a36Sopenharmony_ci d = asd_read_reg_dword(asd_ha, CTXDOMAIN); 49562306a36Sopenharmony_ci d |= 4; 49662306a36Sopenharmony_ci asd_write_reg_dword(asd_ha, CTXDOMAIN, d); 49762306a36Sopenharmony_ci asd_ha->hw_prof.max_ddbs += MAX_DEVS; 49862306a36Sopenharmony_ci} 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_cistatic int asd_extend_devctx(struct asd_ha_struct *asd_ha) 50162306a36Sopenharmony_ci{ 50262306a36Sopenharmony_ci dma_addr_t dma_handle; 50362306a36Sopenharmony_ci unsigned long dma_addr; 50462306a36Sopenharmony_ci u32 d; 50562306a36Sopenharmony_ci int size; 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci asd_extend_devctx_ocm(asd_ha); 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci asd_ha->hw_prof.ddb_ext = NULL; 51062306a36Sopenharmony_ci if (max_devs <= asd_ha->hw_prof.max_ddbs || max_devs > 0xFFFF) { 51162306a36Sopenharmony_ci max_devs = asd_ha->hw_prof.max_ddbs; 51262306a36Sopenharmony_ci return 0; 51362306a36Sopenharmony_ci } 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci size = (max_devs - asd_ha->hw_prof.max_ddbs + 1) * ASD_DDB_SIZE; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci asd_ha->hw_prof.ddb_ext = asd_alloc_coherent(asd_ha, size, GFP_KERNEL); 51862306a36Sopenharmony_ci if (!asd_ha->hw_prof.ddb_ext) { 51962306a36Sopenharmony_ci asd_printk("couldn't allocate memory for %d devices\n", 52062306a36Sopenharmony_ci max_devs); 52162306a36Sopenharmony_ci max_devs = asd_ha->hw_prof.max_ddbs; 52262306a36Sopenharmony_ci return -ENOMEM; 52362306a36Sopenharmony_ci } 52462306a36Sopenharmony_ci dma_handle = asd_ha->hw_prof.ddb_ext->dma_handle; 52562306a36Sopenharmony_ci dma_addr = ALIGN((unsigned long) dma_handle, ASD_DDB_SIZE); 52662306a36Sopenharmony_ci dma_addr -= asd_ha->hw_prof.max_ddbs * ASD_DDB_SIZE; 52762306a36Sopenharmony_ci dma_handle = (dma_addr_t) dma_addr; 52862306a36Sopenharmony_ci asd_write_reg_addr(asd_ha, DEVCTXBASE, dma_handle); 52962306a36Sopenharmony_ci d = asd_read_reg_dword(asd_ha, CTXDOMAIN); 53062306a36Sopenharmony_ci d &= ~4; 53162306a36Sopenharmony_ci asd_write_reg_dword(asd_ha, CTXDOMAIN, d); 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci asd_ha->hw_prof.max_ddbs = max_devs; 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci return 0; 53662306a36Sopenharmony_ci} 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_cistatic int asd_extend_cmdctx(struct asd_ha_struct *asd_ha) 53962306a36Sopenharmony_ci{ 54062306a36Sopenharmony_ci dma_addr_t dma_handle; 54162306a36Sopenharmony_ci unsigned long dma_addr; 54262306a36Sopenharmony_ci u32 d; 54362306a36Sopenharmony_ci int size; 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci asd_ha->hw_prof.scb_ext = NULL; 54662306a36Sopenharmony_ci if (max_cmnds <= asd_ha->hw_prof.max_scbs || max_cmnds > 0xFFFF) { 54762306a36Sopenharmony_ci max_cmnds = asd_ha->hw_prof.max_scbs; 54862306a36Sopenharmony_ci return 0; 54962306a36Sopenharmony_ci } 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci size = (max_cmnds - asd_ha->hw_prof.max_scbs + 1) * ASD_SCB_SIZE; 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci asd_ha->hw_prof.scb_ext = asd_alloc_coherent(asd_ha, size, GFP_KERNEL); 55462306a36Sopenharmony_ci if (!asd_ha->hw_prof.scb_ext) { 55562306a36Sopenharmony_ci asd_printk("couldn't allocate memory for %d commands\n", 55662306a36Sopenharmony_ci max_cmnds); 55762306a36Sopenharmony_ci max_cmnds = asd_ha->hw_prof.max_scbs; 55862306a36Sopenharmony_ci return -ENOMEM; 55962306a36Sopenharmony_ci } 56062306a36Sopenharmony_ci dma_handle = asd_ha->hw_prof.scb_ext->dma_handle; 56162306a36Sopenharmony_ci dma_addr = ALIGN((unsigned long) dma_handle, ASD_SCB_SIZE); 56262306a36Sopenharmony_ci dma_addr -= asd_ha->hw_prof.max_scbs * ASD_SCB_SIZE; 56362306a36Sopenharmony_ci dma_handle = (dma_addr_t) dma_addr; 56462306a36Sopenharmony_ci asd_write_reg_addr(asd_ha, CMDCTXBASE, dma_handle); 56562306a36Sopenharmony_ci d = asd_read_reg_dword(asd_ha, CTXDOMAIN); 56662306a36Sopenharmony_ci d &= ~1; 56762306a36Sopenharmony_ci asd_write_reg_dword(asd_ha, CTXDOMAIN, d); 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci asd_ha->hw_prof.max_scbs = max_cmnds; 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci return 0; 57262306a36Sopenharmony_ci} 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci/** 57562306a36Sopenharmony_ci * asd_init_ctxmem -- initialize context memory 57662306a36Sopenharmony_ci * @asd_ha: pointer to host adapter structure 57762306a36Sopenharmony_ci * 57862306a36Sopenharmony_ci * This function sets the maximum number of SCBs and 57962306a36Sopenharmony_ci * DDBs which can be used by the sequencer. This is normally 58062306a36Sopenharmony_ci * 512 and 128 respectively. If support for more SCBs or more DDBs 58162306a36Sopenharmony_ci * is required then CMDCTXBASE, DEVCTXBASE and CTXDOMAIN are 58262306a36Sopenharmony_ci * initialized here to extend context memory to point to host memory, 58362306a36Sopenharmony_ci * thus allowing unlimited support for SCBs and DDBs -- only limited 58462306a36Sopenharmony_ci * by host memory. 58562306a36Sopenharmony_ci */ 58662306a36Sopenharmony_cistatic int asd_init_ctxmem(struct asd_ha_struct *asd_ha) 58762306a36Sopenharmony_ci{ 58862306a36Sopenharmony_ci int bitmap_bytes; 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci asd_get_max_scb_ddb(asd_ha); 59162306a36Sopenharmony_ci asd_extend_devctx(asd_ha); 59262306a36Sopenharmony_ci asd_extend_cmdctx(asd_ha); 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci /* The kernel wants bitmaps to be unsigned long sized. */ 59562306a36Sopenharmony_ci bitmap_bytes = (asd_ha->hw_prof.max_ddbs+7)/8; 59662306a36Sopenharmony_ci bitmap_bytes = BITS_TO_LONGS(bitmap_bytes*8)*sizeof(unsigned long); 59762306a36Sopenharmony_ci asd_ha->hw_prof.ddb_bitmap = kzalloc(bitmap_bytes, GFP_KERNEL); 59862306a36Sopenharmony_ci if (!asd_ha->hw_prof.ddb_bitmap) 59962306a36Sopenharmony_ci return -ENOMEM; 60062306a36Sopenharmony_ci spin_lock_init(&asd_ha->hw_prof.ddb_lock); 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci return 0; 60362306a36Sopenharmony_ci} 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ciint asd_init_hw(struct asd_ha_struct *asd_ha) 60662306a36Sopenharmony_ci{ 60762306a36Sopenharmony_ci int err; 60862306a36Sopenharmony_ci u32 v; 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci err = asd_init_sw(asd_ha); 61162306a36Sopenharmony_ci if (err) 61262306a36Sopenharmony_ci return err; 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci err = pci_read_config_dword(asd_ha->pcidev, PCIC_HSTPCIX_CNTRL, &v); 61562306a36Sopenharmony_ci if (err) { 61662306a36Sopenharmony_ci asd_printk("couldn't read PCIC_HSTPCIX_CNTRL of %s\n", 61762306a36Sopenharmony_ci pci_name(asd_ha->pcidev)); 61862306a36Sopenharmony_ci return err; 61962306a36Sopenharmony_ci } 62062306a36Sopenharmony_ci err = pci_write_config_dword(asd_ha->pcidev, PCIC_HSTPCIX_CNTRL, 62162306a36Sopenharmony_ci v | SC_TMR_DIS); 62262306a36Sopenharmony_ci if (err) { 62362306a36Sopenharmony_ci asd_printk("couldn't disable split completion timer of %s\n", 62462306a36Sopenharmony_ci pci_name(asd_ha->pcidev)); 62562306a36Sopenharmony_ci return err; 62662306a36Sopenharmony_ci } 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci err = asd_read_ocm(asd_ha); 62962306a36Sopenharmony_ci if (err) { 63062306a36Sopenharmony_ci asd_printk("couldn't read ocm(%d)\n", err); 63162306a36Sopenharmony_ci /* While suspicios, it is not an error that we 63262306a36Sopenharmony_ci * couldn't read the OCM. */ 63362306a36Sopenharmony_ci } 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci err = asd_read_flash(asd_ha); 63662306a36Sopenharmony_ci if (err) { 63762306a36Sopenharmony_ci asd_printk("couldn't read flash(%d)\n", err); 63862306a36Sopenharmony_ci /* While suspicios, it is not an error that we 63962306a36Sopenharmony_ci * couldn't read FLASH memory. 64062306a36Sopenharmony_ci */ 64162306a36Sopenharmony_ci } 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci asd_init_ctxmem(asd_ha); 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci if (asd_get_user_sas_addr(asd_ha)) { 64662306a36Sopenharmony_ci asd_printk("No SAS Address provided for %s\n", 64762306a36Sopenharmony_ci pci_name(asd_ha->pcidev)); 64862306a36Sopenharmony_ci err = -ENODEV; 64962306a36Sopenharmony_ci goto Out; 65062306a36Sopenharmony_ci } 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci asd_propagate_sas_addr(asd_ha); 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci err = asd_init_phys(asd_ha); 65562306a36Sopenharmony_ci if (err) { 65662306a36Sopenharmony_ci asd_printk("couldn't initialize phys for %s\n", 65762306a36Sopenharmony_ci pci_name(asd_ha->pcidev)); 65862306a36Sopenharmony_ci goto Out; 65962306a36Sopenharmony_ci } 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci asd_init_ports(asd_ha); 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci err = asd_init_scbs(asd_ha); 66462306a36Sopenharmony_ci if (err) { 66562306a36Sopenharmony_ci asd_printk("couldn't initialize scbs for %s\n", 66662306a36Sopenharmony_ci pci_name(asd_ha->pcidev)); 66762306a36Sopenharmony_ci goto Out; 66862306a36Sopenharmony_ci } 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci err = asd_init_dl(asd_ha); 67162306a36Sopenharmony_ci if (err) { 67262306a36Sopenharmony_ci asd_printk("couldn't initialize the done list:%d\n", 67362306a36Sopenharmony_ci err); 67462306a36Sopenharmony_ci goto Out; 67562306a36Sopenharmony_ci } 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci err = asd_init_escbs(asd_ha); 67862306a36Sopenharmony_ci if (err) { 67962306a36Sopenharmony_ci asd_printk("couldn't initialize escbs\n"); 68062306a36Sopenharmony_ci goto Out; 68162306a36Sopenharmony_ci } 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci err = asd_init_chip(asd_ha); 68462306a36Sopenharmony_ci if (err) { 68562306a36Sopenharmony_ci asd_printk("couldn't init the chip\n"); 68662306a36Sopenharmony_ci goto Out; 68762306a36Sopenharmony_ci } 68862306a36Sopenharmony_ciOut: 68962306a36Sopenharmony_ci return err; 69062306a36Sopenharmony_ci} 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci/* ---------- Chip reset ---------- */ 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci/** 69562306a36Sopenharmony_ci * asd_chip_reset -- reset the host adapter, etc 69662306a36Sopenharmony_ci * @asd_ha: pointer to host adapter structure of interest 69762306a36Sopenharmony_ci * 69862306a36Sopenharmony_ci * Called from the ISR. Hard reset the chip. Let everything 69962306a36Sopenharmony_ci * timeout. This should be no different than hot-unplugging the 70062306a36Sopenharmony_ci * host adapter. Once everything times out we'll init the chip with 70162306a36Sopenharmony_ci * a call to asd_init_chip() and enable interrupts with asd_enable_ints(). 70262306a36Sopenharmony_ci * XXX finish. 70362306a36Sopenharmony_ci */ 70462306a36Sopenharmony_cistatic void asd_chip_reset(struct asd_ha_struct *asd_ha) 70562306a36Sopenharmony_ci{ 70662306a36Sopenharmony_ci ASD_DPRINTK("chip reset for %s\n", pci_name(asd_ha->pcidev)); 70762306a36Sopenharmony_ci asd_chip_hardrst(asd_ha); 70862306a36Sopenharmony_ci} 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci/* ---------- Done List Routines ---------- */ 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_cistatic void asd_dl_tasklet_handler(unsigned long data) 71362306a36Sopenharmony_ci{ 71462306a36Sopenharmony_ci struct asd_ha_struct *asd_ha = (struct asd_ha_struct *) data; 71562306a36Sopenharmony_ci struct asd_seq_data *seq = &asd_ha->seq; 71662306a36Sopenharmony_ci unsigned long flags; 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci while (1) { 71962306a36Sopenharmony_ci struct done_list_struct *dl = &seq->dl[seq->dl_next]; 72062306a36Sopenharmony_ci struct asd_ascb *ascb; 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci if ((dl->toggle & DL_TOGGLE_MASK) != seq->dl_toggle) 72362306a36Sopenharmony_ci break; 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci /* find the aSCB */ 72662306a36Sopenharmony_ci spin_lock_irqsave(&seq->tc_index_lock, flags); 72762306a36Sopenharmony_ci ascb = asd_tc_index_find(seq, (int)le16_to_cpu(dl->index)); 72862306a36Sopenharmony_ci spin_unlock_irqrestore(&seq->tc_index_lock, flags); 72962306a36Sopenharmony_ci if (unlikely(!ascb)) { 73062306a36Sopenharmony_ci ASD_DPRINTK("BUG:sequencer:dl:no ascb?!\n"); 73162306a36Sopenharmony_ci goto next_1; 73262306a36Sopenharmony_ci } else if (ascb->scb->header.opcode == EMPTY_SCB) { 73362306a36Sopenharmony_ci goto out; 73462306a36Sopenharmony_ci } else if (!ascb->uldd_timer && !del_timer(&ascb->timer)) { 73562306a36Sopenharmony_ci goto next_1; 73662306a36Sopenharmony_ci } 73762306a36Sopenharmony_ci spin_lock_irqsave(&seq->pend_q_lock, flags); 73862306a36Sopenharmony_ci list_del_init(&ascb->list); 73962306a36Sopenharmony_ci seq->pending--; 74062306a36Sopenharmony_ci spin_unlock_irqrestore(&seq->pend_q_lock, flags); 74162306a36Sopenharmony_ci out: 74262306a36Sopenharmony_ci ascb->tasklet_complete(ascb, dl); 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci next_1: 74562306a36Sopenharmony_ci seq->dl_next = (seq->dl_next + 1) & (ASD_DL_SIZE-1); 74662306a36Sopenharmony_ci if (!seq->dl_next) 74762306a36Sopenharmony_ci seq->dl_toggle ^= DL_TOGGLE_MASK; 74862306a36Sopenharmony_ci } 74962306a36Sopenharmony_ci} 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci/* ---------- Interrupt Service Routines ---------- */ 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci/** 75462306a36Sopenharmony_ci * asd_process_donelist_isr -- schedule processing of done list entries 75562306a36Sopenharmony_ci * @asd_ha: pointer to host adapter structure 75662306a36Sopenharmony_ci */ 75762306a36Sopenharmony_cistatic void asd_process_donelist_isr(struct asd_ha_struct *asd_ha) 75862306a36Sopenharmony_ci{ 75962306a36Sopenharmony_ci tasklet_schedule(&asd_ha->seq.dl_tasklet); 76062306a36Sopenharmony_ci} 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci/** 76362306a36Sopenharmony_ci * asd_com_sas_isr -- process device communication interrupt (COMINT) 76462306a36Sopenharmony_ci * @asd_ha: pointer to host adapter structure 76562306a36Sopenharmony_ci */ 76662306a36Sopenharmony_cistatic void asd_com_sas_isr(struct asd_ha_struct *asd_ha) 76762306a36Sopenharmony_ci{ 76862306a36Sopenharmony_ci u32 comstat = asd_read_reg_dword(asd_ha, COMSTAT); 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci /* clear COMSTAT int */ 77162306a36Sopenharmony_ci asd_write_reg_dword(asd_ha, COMSTAT, 0xFFFFFFFF); 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci if (comstat & CSBUFPERR) { 77462306a36Sopenharmony_ci asd_printk("%s: command/status buffer dma parity error\n", 77562306a36Sopenharmony_ci pci_name(asd_ha->pcidev)); 77662306a36Sopenharmony_ci } else if (comstat & CSERR) { 77762306a36Sopenharmony_ci int i; 77862306a36Sopenharmony_ci u32 dmaerr = asd_read_reg_dword(asd_ha, DMAERR); 77962306a36Sopenharmony_ci dmaerr &= 0xFF; 78062306a36Sopenharmony_ci asd_printk("%s: command/status dma error, DMAERR: 0x%02x, " 78162306a36Sopenharmony_ci "CSDMAADR: 0x%04x, CSDMAADR+4: 0x%04x\n", 78262306a36Sopenharmony_ci pci_name(asd_ha->pcidev), 78362306a36Sopenharmony_ci dmaerr, 78462306a36Sopenharmony_ci asd_read_reg_dword(asd_ha, CSDMAADR), 78562306a36Sopenharmony_ci asd_read_reg_dword(asd_ha, CSDMAADR+4)); 78662306a36Sopenharmony_ci asd_printk("CSBUFFER:\n"); 78762306a36Sopenharmony_ci for (i = 0; i < 8; i++) { 78862306a36Sopenharmony_ci asd_printk("%08x %08x %08x %08x\n", 78962306a36Sopenharmony_ci asd_read_reg_dword(asd_ha, CSBUFFER), 79062306a36Sopenharmony_ci asd_read_reg_dword(asd_ha, CSBUFFER+4), 79162306a36Sopenharmony_ci asd_read_reg_dword(asd_ha, CSBUFFER+8), 79262306a36Sopenharmony_ci asd_read_reg_dword(asd_ha, CSBUFFER+12)); 79362306a36Sopenharmony_ci } 79462306a36Sopenharmony_ci asd_dump_seq_state(asd_ha, 0); 79562306a36Sopenharmony_ci } else if (comstat & OVLYERR) { 79662306a36Sopenharmony_ci u32 dmaerr = asd_read_reg_dword(asd_ha, DMAERR); 79762306a36Sopenharmony_ci dmaerr = (dmaerr >> 8) & 0xFF; 79862306a36Sopenharmony_ci asd_printk("%s: overlay dma error:0x%x\n", 79962306a36Sopenharmony_ci pci_name(asd_ha->pcidev), 80062306a36Sopenharmony_ci dmaerr); 80162306a36Sopenharmony_ci } 80262306a36Sopenharmony_ci asd_chip_reset(asd_ha); 80362306a36Sopenharmony_ci} 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_cistatic void asd_arp2_err(struct asd_ha_struct *asd_ha, u32 dchstatus) 80662306a36Sopenharmony_ci{ 80762306a36Sopenharmony_ci static const char *halt_code[256] = { 80862306a36Sopenharmony_ci "UNEXPECTED_INTERRUPT0", 80962306a36Sopenharmony_ci "UNEXPECTED_INTERRUPT1", 81062306a36Sopenharmony_ci "UNEXPECTED_INTERRUPT2", 81162306a36Sopenharmony_ci "UNEXPECTED_INTERRUPT3", 81262306a36Sopenharmony_ci "UNEXPECTED_INTERRUPT4", 81362306a36Sopenharmony_ci "UNEXPECTED_INTERRUPT5", 81462306a36Sopenharmony_ci "UNEXPECTED_INTERRUPT6", 81562306a36Sopenharmony_ci "UNEXPECTED_INTERRUPT7", 81662306a36Sopenharmony_ci "UNEXPECTED_INTERRUPT8", 81762306a36Sopenharmony_ci "UNEXPECTED_INTERRUPT9", 81862306a36Sopenharmony_ci "UNEXPECTED_INTERRUPT10", 81962306a36Sopenharmony_ci [11 ... 19] = "unknown[11,19]", 82062306a36Sopenharmony_ci "NO_FREE_SCB_AVAILABLE", 82162306a36Sopenharmony_ci "INVALID_SCB_OPCODE", 82262306a36Sopenharmony_ci "INVALID_MBX_OPCODE", 82362306a36Sopenharmony_ci "INVALID_ATA_STATE", 82462306a36Sopenharmony_ci "ATA_QUEUE_FULL", 82562306a36Sopenharmony_ci "ATA_TAG_TABLE_FAULT", 82662306a36Sopenharmony_ci "ATA_TAG_MASK_FAULT", 82762306a36Sopenharmony_ci "BAD_LINK_QUEUE_STATE", 82862306a36Sopenharmony_ci "DMA2CHIM_QUEUE_ERROR", 82962306a36Sopenharmony_ci "EMPTY_SCB_LIST_FULL", 83062306a36Sopenharmony_ci "unknown[30]", 83162306a36Sopenharmony_ci "IN_USE_SCB_ON_FREE_LIST", 83262306a36Sopenharmony_ci "BAD_OPEN_WAIT_STATE", 83362306a36Sopenharmony_ci "INVALID_STP_AFFILIATION", 83462306a36Sopenharmony_ci "unknown[34]", 83562306a36Sopenharmony_ci "EXEC_QUEUE_ERROR", 83662306a36Sopenharmony_ci "TOO_MANY_EMPTIES_NEEDED", 83762306a36Sopenharmony_ci "EMPTY_REQ_QUEUE_ERROR", 83862306a36Sopenharmony_ci "Q_MONIRTT_MGMT_ERROR", 83962306a36Sopenharmony_ci "TARGET_MODE_FLOW_ERROR", 84062306a36Sopenharmony_ci "DEVICE_QUEUE_NOT_FOUND", 84162306a36Sopenharmony_ci "START_IRTT_TIMER_ERROR", 84262306a36Sopenharmony_ci "ABORT_TASK_ILLEGAL_REQ", 84362306a36Sopenharmony_ci [43 ... 255] = "unknown[43,255]" 84462306a36Sopenharmony_ci }; 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci if (dchstatus & CSEQINT) { 84762306a36Sopenharmony_ci u32 arp2int = asd_read_reg_dword(asd_ha, CARP2INT); 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci if (arp2int & (ARP2WAITTO|ARP2ILLOPC|ARP2PERR|ARP2CIOPERR)) { 85062306a36Sopenharmony_ci asd_printk("%s: CSEQ arp2int:0x%x\n", 85162306a36Sopenharmony_ci pci_name(asd_ha->pcidev), 85262306a36Sopenharmony_ci arp2int); 85362306a36Sopenharmony_ci } else if (arp2int & ARP2HALTC) 85462306a36Sopenharmony_ci asd_printk("%s: CSEQ halted: %s\n", 85562306a36Sopenharmony_ci pci_name(asd_ha->pcidev), 85662306a36Sopenharmony_ci halt_code[(arp2int>>16)&0xFF]); 85762306a36Sopenharmony_ci else 85862306a36Sopenharmony_ci asd_printk("%s: CARP2INT:0x%x\n", 85962306a36Sopenharmony_ci pci_name(asd_ha->pcidev), 86062306a36Sopenharmony_ci arp2int); 86162306a36Sopenharmony_ci } 86262306a36Sopenharmony_ci if (dchstatus & LSEQINT_MASK) { 86362306a36Sopenharmony_ci int lseq; 86462306a36Sopenharmony_ci u8 lseq_mask = dchstatus & LSEQINT_MASK; 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_ci for_each_sequencer(lseq_mask, lseq_mask, lseq) { 86762306a36Sopenharmony_ci u32 arp2int = asd_read_reg_dword(asd_ha, 86862306a36Sopenharmony_ci LmARP2INT(lseq)); 86962306a36Sopenharmony_ci if (arp2int & (ARP2WAITTO | ARP2ILLOPC | ARP2PERR 87062306a36Sopenharmony_ci | ARP2CIOPERR)) { 87162306a36Sopenharmony_ci asd_printk("%s: LSEQ%d arp2int:0x%x\n", 87262306a36Sopenharmony_ci pci_name(asd_ha->pcidev), 87362306a36Sopenharmony_ci lseq, arp2int); 87462306a36Sopenharmony_ci /* XXX we should only do lseq reset */ 87562306a36Sopenharmony_ci } else if (arp2int & ARP2HALTC) 87662306a36Sopenharmony_ci asd_printk("%s: LSEQ%d halted: %s\n", 87762306a36Sopenharmony_ci pci_name(asd_ha->pcidev), 87862306a36Sopenharmony_ci lseq,halt_code[(arp2int>>16)&0xFF]); 87962306a36Sopenharmony_ci else 88062306a36Sopenharmony_ci asd_printk("%s: LSEQ%d ARP2INT:0x%x\n", 88162306a36Sopenharmony_ci pci_name(asd_ha->pcidev), lseq, 88262306a36Sopenharmony_ci arp2int); 88362306a36Sopenharmony_ci } 88462306a36Sopenharmony_ci } 88562306a36Sopenharmony_ci asd_chip_reset(asd_ha); 88662306a36Sopenharmony_ci} 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci/** 88962306a36Sopenharmony_ci * asd_dch_sas_isr -- process device channel interrupt (DEVINT) 89062306a36Sopenharmony_ci * @asd_ha: pointer to host adapter structure 89162306a36Sopenharmony_ci */ 89262306a36Sopenharmony_cistatic void asd_dch_sas_isr(struct asd_ha_struct *asd_ha) 89362306a36Sopenharmony_ci{ 89462306a36Sopenharmony_ci u32 dchstatus = asd_read_reg_dword(asd_ha, DCHSTATUS); 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_ci if (dchstatus & CFIFTOERR) { 89762306a36Sopenharmony_ci asd_printk("%s: CFIFTOERR\n", pci_name(asd_ha->pcidev)); 89862306a36Sopenharmony_ci asd_chip_reset(asd_ha); 89962306a36Sopenharmony_ci } else 90062306a36Sopenharmony_ci asd_arp2_err(asd_ha, dchstatus); 90162306a36Sopenharmony_ci} 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_ci/** 90462306a36Sopenharmony_ci * asd_rbi_exsi_isr -- process external system interface interrupt (INITERR) 90562306a36Sopenharmony_ci * @asd_ha: pointer to host adapter structure 90662306a36Sopenharmony_ci */ 90762306a36Sopenharmony_cistatic void asd_rbi_exsi_isr(struct asd_ha_struct *asd_ha) 90862306a36Sopenharmony_ci{ 90962306a36Sopenharmony_ci u32 stat0r = asd_read_reg_dword(asd_ha, ASISTAT0R); 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_ci if (!(stat0r & ASIERR)) { 91262306a36Sopenharmony_ci asd_printk("hmm, EXSI interrupted but no error?\n"); 91362306a36Sopenharmony_ci return; 91462306a36Sopenharmony_ci } 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ci if (stat0r & ASIFMTERR) { 91762306a36Sopenharmony_ci asd_printk("ASI SEEPROM format error for %s\n", 91862306a36Sopenharmony_ci pci_name(asd_ha->pcidev)); 91962306a36Sopenharmony_ci } else if (stat0r & ASISEECHKERR) { 92062306a36Sopenharmony_ci u32 stat1r = asd_read_reg_dword(asd_ha, ASISTAT1R); 92162306a36Sopenharmony_ci asd_printk("ASI SEEPROM checksum 0x%x error for %s\n", 92262306a36Sopenharmony_ci stat1r & CHECKSUM_MASK, 92362306a36Sopenharmony_ci pci_name(asd_ha->pcidev)); 92462306a36Sopenharmony_ci } else { 92562306a36Sopenharmony_ci u32 statr = asd_read_reg_dword(asd_ha, ASIERRSTATR); 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci if (!(statr & CPI2ASIMSTERR_MASK)) { 92862306a36Sopenharmony_ci ASD_DPRINTK("hmm, ASIERR?\n"); 92962306a36Sopenharmony_ci return; 93062306a36Sopenharmony_ci } else { 93162306a36Sopenharmony_ci u32 addr = asd_read_reg_dword(asd_ha, ASIERRADDR); 93262306a36Sopenharmony_ci u32 data = asd_read_reg_dword(asd_ha, ASIERRDATAR); 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_ci asd_printk("%s: CPI2 xfer err: addr: 0x%x, wdata: 0x%x, " 93562306a36Sopenharmony_ci "count: 0x%x, byteen: 0x%x, targerr: 0x%x " 93662306a36Sopenharmony_ci "master id: 0x%x, master err: 0x%x\n", 93762306a36Sopenharmony_ci pci_name(asd_ha->pcidev), 93862306a36Sopenharmony_ci addr, data, 93962306a36Sopenharmony_ci (statr & CPI2ASIBYTECNT_MASK) >> 16, 94062306a36Sopenharmony_ci (statr & CPI2ASIBYTEEN_MASK) >> 12, 94162306a36Sopenharmony_ci (statr & CPI2ASITARGERR_MASK) >> 8, 94262306a36Sopenharmony_ci (statr & CPI2ASITARGMID_MASK) >> 4, 94362306a36Sopenharmony_ci (statr & CPI2ASIMSTERR_MASK)); 94462306a36Sopenharmony_ci } 94562306a36Sopenharmony_ci } 94662306a36Sopenharmony_ci asd_chip_reset(asd_ha); 94762306a36Sopenharmony_ci} 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_ci/** 95062306a36Sopenharmony_ci * asd_hst_pcix_isr -- process host interface interrupts 95162306a36Sopenharmony_ci * @asd_ha: pointer to host adapter structure 95262306a36Sopenharmony_ci * 95362306a36Sopenharmony_ci * Asserted on PCIX errors: target abort, etc. 95462306a36Sopenharmony_ci */ 95562306a36Sopenharmony_cistatic void asd_hst_pcix_isr(struct asd_ha_struct *asd_ha) 95662306a36Sopenharmony_ci{ 95762306a36Sopenharmony_ci u16 status; 95862306a36Sopenharmony_ci u32 pcix_status; 95962306a36Sopenharmony_ci u32 ecc_status; 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_ci pci_read_config_word(asd_ha->pcidev, PCI_STATUS, &status); 96262306a36Sopenharmony_ci pci_read_config_dword(asd_ha->pcidev, PCIX_STATUS, &pcix_status); 96362306a36Sopenharmony_ci pci_read_config_dword(asd_ha->pcidev, ECC_CTRL_STAT, &ecc_status); 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_ci if (status & PCI_STATUS_DETECTED_PARITY) 96662306a36Sopenharmony_ci asd_printk("parity error for %s\n", pci_name(asd_ha->pcidev)); 96762306a36Sopenharmony_ci else if (status & PCI_STATUS_REC_MASTER_ABORT) 96862306a36Sopenharmony_ci asd_printk("master abort for %s\n", pci_name(asd_ha->pcidev)); 96962306a36Sopenharmony_ci else if (status & PCI_STATUS_REC_TARGET_ABORT) 97062306a36Sopenharmony_ci asd_printk("target abort for %s\n", pci_name(asd_ha->pcidev)); 97162306a36Sopenharmony_ci else if (status & PCI_STATUS_PARITY) 97262306a36Sopenharmony_ci asd_printk("data parity for %s\n", pci_name(asd_ha->pcidev)); 97362306a36Sopenharmony_ci else if (pcix_status & RCV_SCE) { 97462306a36Sopenharmony_ci asd_printk("received split completion error for %s\n", 97562306a36Sopenharmony_ci pci_name(asd_ha->pcidev)); 97662306a36Sopenharmony_ci pci_write_config_dword(asd_ha->pcidev,PCIX_STATUS,pcix_status); 97762306a36Sopenharmony_ci /* XXX: Abort task? */ 97862306a36Sopenharmony_ci return; 97962306a36Sopenharmony_ci } else if (pcix_status & UNEXP_SC) { 98062306a36Sopenharmony_ci asd_printk("unexpected split completion for %s\n", 98162306a36Sopenharmony_ci pci_name(asd_ha->pcidev)); 98262306a36Sopenharmony_ci pci_write_config_dword(asd_ha->pcidev,PCIX_STATUS,pcix_status); 98362306a36Sopenharmony_ci /* ignore */ 98462306a36Sopenharmony_ci return; 98562306a36Sopenharmony_ci } else if (pcix_status & SC_DISCARD) 98662306a36Sopenharmony_ci asd_printk("split completion discarded for %s\n", 98762306a36Sopenharmony_ci pci_name(asd_ha->pcidev)); 98862306a36Sopenharmony_ci else if (ecc_status & UNCOR_ECCERR) 98962306a36Sopenharmony_ci asd_printk("uncorrectable ECC error for %s\n", 99062306a36Sopenharmony_ci pci_name(asd_ha->pcidev)); 99162306a36Sopenharmony_ci asd_chip_reset(asd_ha); 99262306a36Sopenharmony_ci} 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_ci/** 99562306a36Sopenharmony_ci * asd_hw_isr -- host adapter interrupt service routine 99662306a36Sopenharmony_ci * @irq: ignored 99762306a36Sopenharmony_ci * @dev_id: pointer to host adapter structure 99862306a36Sopenharmony_ci * 99962306a36Sopenharmony_ci * The ISR processes done list entries and level 3 error handling. 100062306a36Sopenharmony_ci */ 100162306a36Sopenharmony_ciirqreturn_t asd_hw_isr(int irq, void *dev_id) 100262306a36Sopenharmony_ci{ 100362306a36Sopenharmony_ci struct asd_ha_struct *asd_ha = dev_id; 100462306a36Sopenharmony_ci u32 chimint = asd_read_reg_dword(asd_ha, CHIMINT); 100562306a36Sopenharmony_ci 100662306a36Sopenharmony_ci if (!chimint) 100762306a36Sopenharmony_ci return IRQ_NONE; 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_ci asd_write_reg_dword(asd_ha, CHIMINT, chimint); 101062306a36Sopenharmony_ci (void) asd_read_reg_dword(asd_ha, CHIMINT); 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_ci if (chimint & DLAVAIL) 101362306a36Sopenharmony_ci asd_process_donelist_isr(asd_ha); 101462306a36Sopenharmony_ci if (chimint & COMINT) 101562306a36Sopenharmony_ci asd_com_sas_isr(asd_ha); 101662306a36Sopenharmony_ci if (chimint & DEVINT) 101762306a36Sopenharmony_ci asd_dch_sas_isr(asd_ha); 101862306a36Sopenharmony_ci if (chimint & INITERR) 101962306a36Sopenharmony_ci asd_rbi_exsi_isr(asd_ha); 102062306a36Sopenharmony_ci if (chimint & HOSTERR) 102162306a36Sopenharmony_ci asd_hst_pcix_isr(asd_ha); 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_ci return IRQ_HANDLED; 102462306a36Sopenharmony_ci} 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_ci/* ---------- SCB handling ---------- */ 102762306a36Sopenharmony_ci 102862306a36Sopenharmony_cistatic struct asd_ascb *asd_ascb_alloc(struct asd_ha_struct *asd_ha, 102962306a36Sopenharmony_ci gfp_t gfp_flags) 103062306a36Sopenharmony_ci{ 103162306a36Sopenharmony_ci extern struct kmem_cache *asd_ascb_cache; 103262306a36Sopenharmony_ci struct asd_seq_data *seq = &asd_ha->seq; 103362306a36Sopenharmony_ci struct asd_ascb *ascb; 103462306a36Sopenharmony_ci unsigned long flags; 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_ci ascb = kmem_cache_zalloc(asd_ascb_cache, gfp_flags); 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_ci if (ascb) { 103962306a36Sopenharmony_ci ascb->dma_scb.size = sizeof(struct scb); 104062306a36Sopenharmony_ci ascb->dma_scb.vaddr = dma_pool_zalloc(asd_ha->scb_pool, 104162306a36Sopenharmony_ci gfp_flags, 104262306a36Sopenharmony_ci &ascb->dma_scb.dma_handle); 104362306a36Sopenharmony_ci if (!ascb->dma_scb.vaddr) { 104462306a36Sopenharmony_ci kmem_cache_free(asd_ascb_cache, ascb); 104562306a36Sopenharmony_ci return NULL; 104662306a36Sopenharmony_ci } 104762306a36Sopenharmony_ci asd_init_ascb(asd_ha, ascb); 104862306a36Sopenharmony_ci 104962306a36Sopenharmony_ci spin_lock_irqsave(&seq->tc_index_lock, flags); 105062306a36Sopenharmony_ci ascb->tc_index = asd_tc_index_get(seq, ascb); 105162306a36Sopenharmony_ci spin_unlock_irqrestore(&seq->tc_index_lock, flags); 105262306a36Sopenharmony_ci if (ascb->tc_index == -1) 105362306a36Sopenharmony_ci goto undo; 105462306a36Sopenharmony_ci 105562306a36Sopenharmony_ci ascb->scb->header.index = cpu_to_le16((u16)ascb->tc_index); 105662306a36Sopenharmony_ci } 105762306a36Sopenharmony_ci 105862306a36Sopenharmony_ci return ascb; 105962306a36Sopenharmony_ciundo: 106062306a36Sopenharmony_ci dma_pool_free(asd_ha->scb_pool, ascb->dma_scb.vaddr, 106162306a36Sopenharmony_ci ascb->dma_scb.dma_handle); 106262306a36Sopenharmony_ci kmem_cache_free(asd_ascb_cache, ascb); 106362306a36Sopenharmony_ci ASD_DPRINTK("no index for ascb\n"); 106462306a36Sopenharmony_ci return NULL; 106562306a36Sopenharmony_ci} 106662306a36Sopenharmony_ci 106762306a36Sopenharmony_ci/** 106862306a36Sopenharmony_ci * asd_ascb_alloc_list -- allocate a list of aSCBs 106962306a36Sopenharmony_ci * @asd_ha: pointer to host adapter structure 107062306a36Sopenharmony_ci * @num: pointer to integer number of aSCBs 107162306a36Sopenharmony_ci * @gfp_flags: GFP_ flags. 107262306a36Sopenharmony_ci * 107362306a36Sopenharmony_ci * This is the only function which is used to allocate aSCBs. 107462306a36Sopenharmony_ci * It can allocate one or many. If more than one, then they form 107562306a36Sopenharmony_ci * a linked list in two ways: by their list field of the ascb struct 107662306a36Sopenharmony_ci * and by the next_scb field of the scb_header. 107762306a36Sopenharmony_ci * 107862306a36Sopenharmony_ci * Returns NULL if no memory was available, else pointer to a list 107962306a36Sopenharmony_ci * of ascbs. When this function returns, @num would be the number 108062306a36Sopenharmony_ci * of SCBs which were not able to be allocated, 0 if all requested 108162306a36Sopenharmony_ci * were able to be allocated. 108262306a36Sopenharmony_ci */ 108362306a36Sopenharmony_cistruct asd_ascb *asd_ascb_alloc_list(struct asd_ha_struct 108462306a36Sopenharmony_ci *asd_ha, int *num, 108562306a36Sopenharmony_ci gfp_t gfp_flags) 108662306a36Sopenharmony_ci{ 108762306a36Sopenharmony_ci struct asd_ascb *first = NULL; 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_ci for ( ; *num > 0; --*num) { 109062306a36Sopenharmony_ci struct asd_ascb *ascb = asd_ascb_alloc(asd_ha, gfp_flags); 109162306a36Sopenharmony_ci 109262306a36Sopenharmony_ci if (!ascb) 109362306a36Sopenharmony_ci break; 109462306a36Sopenharmony_ci else if (!first) 109562306a36Sopenharmony_ci first = ascb; 109662306a36Sopenharmony_ci else { 109762306a36Sopenharmony_ci struct asd_ascb *last = list_entry(first->list.prev, 109862306a36Sopenharmony_ci struct asd_ascb, 109962306a36Sopenharmony_ci list); 110062306a36Sopenharmony_ci list_add_tail(&ascb->list, &first->list); 110162306a36Sopenharmony_ci last->scb->header.next_scb = 110262306a36Sopenharmony_ci cpu_to_le64(((u64)ascb->dma_scb.dma_handle)); 110362306a36Sopenharmony_ci } 110462306a36Sopenharmony_ci } 110562306a36Sopenharmony_ci 110662306a36Sopenharmony_ci return first; 110762306a36Sopenharmony_ci} 110862306a36Sopenharmony_ci 110962306a36Sopenharmony_ci/** 111062306a36Sopenharmony_ci * asd_swap_head_scb -- swap the head scb 111162306a36Sopenharmony_ci * @asd_ha: pointer to host adapter structure 111262306a36Sopenharmony_ci * @ascb: pointer to the head of an ascb list 111362306a36Sopenharmony_ci * 111462306a36Sopenharmony_ci * The sequencer knows the DMA address of the next SCB to be DMAed to 111562306a36Sopenharmony_ci * the host adapter, from initialization or from the last list DMAed. 111662306a36Sopenharmony_ci * seq->next_scb keeps the address of this SCB. The sequencer will 111762306a36Sopenharmony_ci * DMA to the host adapter this list of SCBs. But the head (first 111862306a36Sopenharmony_ci * element) of this list is not known to the sequencer. Here we swap 111962306a36Sopenharmony_ci * the head of the list with the known SCB (memcpy()). 112062306a36Sopenharmony_ci * Only one memcpy() is required per list so it is in our interest 112162306a36Sopenharmony_ci * to keep the list of SCB as long as possible so that the ratio 112262306a36Sopenharmony_ci * of number of memcpy calls to the number of SCB DMA-ed is as small 112362306a36Sopenharmony_ci * as possible. 112462306a36Sopenharmony_ci * 112562306a36Sopenharmony_ci * LOCKING: called with the pending list lock held. 112662306a36Sopenharmony_ci */ 112762306a36Sopenharmony_cistatic void asd_swap_head_scb(struct asd_ha_struct *asd_ha, 112862306a36Sopenharmony_ci struct asd_ascb *ascb) 112962306a36Sopenharmony_ci{ 113062306a36Sopenharmony_ci struct asd_seq_data *seq = &asd_ha->seq; 113162306a36Sopenharmony_ci struct asd_ascb *last = list_entry(ascb->list.prev, 113262306a36Sopenharmony_ci struct asd_ascb, 113362306a36Sopenharmony_ci list); 113462306a36Sopenharmony_ci struct asd_dma_tok t = ascb->dma_scb; 113562306a36Sopenharmony_ci 113662306a36Sopenharmony_ci memcpy(seq->next_scb.vaddr, ascb->scb, sizeof(*ascb->scb)); 113762306a36Sopenharmony_ci ascb->dma_scb = seq->next_scb; 113862306a36Sopenharmony_ci ascb->scb = ascb->dma_scb.vaddr; 113962306a36Sopenharmony_ci seq->next_scb = t; 114062306a36Sopenharmony_ci last->scb->header.next_scb = 114162306a36Sopenharmony_ci cpu_to_le64(((u64)seq->next_scb.dma_handle)); 114262306a36Sopenharmony_ci} 114362306a36Sopenharmony_ci 114462306a36Sopenharmony_ci/** 114562306a36Sopenharmony_ci * asd_start_scb_timers -- (add and) start timers of SCBs 114662306a36Sopenharmony_ci * @list: pointer to struct list_head of the scbs 114762306a36Sopenharmony_ci * 114862306a36Sopenharmony_ci * If an SCB in the @list has no timer function, assign the default 114962306a36Sopenharmony_ci * one, then start the timer of the SCB. This function is 115062306a36Sopenharmony_ci * intended to be called from asd_post_ascb_list(), just prior to 115162306a36Sopenharmony_ci * posting the SCBs to the sequencer. 115262306a36Sopenharmony_ci */ 115362306a36Sopenharmony_cistatic void asd_start_scb_timers(struct list_head *list) 115462306a36Sopenharmony_ci{ 115562306a36Sopenharmony_ci struct asd_ascb *ascb; 115662306a36Sopenharmony_ci list_for_each_entry(ascb, list, list) { 115762306a36Sopenharmony_ci if (!ascb->uldd_timer) { 115862306a36Sopenharmony_ci ascb->timer.function = asd_ascb_timedout; 115962306a36Sopenharmony_ci ascb->timer.expires = jiffies + AIC94XX_SCB_TIMEOUT; 116062306a36Sopenharmony_ci add_timer(&ascb->timer); 116162306a36Sopenharmony_ci } 116262306a36Sopenharmony_ci } 116362306a36Sopenharmony_ci} 116462306a36Sopenharmony_ci 116562306a36Sopenharmony_ci/** 116662306a36Sopenharmony_ci * asd_post_ascb_list -- post a list of 1 or more aSCBs to the host adapter 116762306a36Sopenharmony_ci * @asd_ha: pointer to a host adapter structure 116862306a36Sopenharmony_ci * @ascb: pointer to the first aSCB in the list 116962306a36Sopenharmony_ci * @num: number of aSCBs in the list (to be posted) 117062306a36Sopenharmony_ci * 117162306a36Sopenharmony_ci * See queueing comment in asd_post_escb_list(). 117262306a36Sopenharmony_ci * 117362306a36Sopenharmony_ci * Additional note on queuing: In order to minimize the ratio of memcpy() 117462306a36Sopenharmony_ci * to the number of ascbs sent, we try to batch-send as many ascbs as possible 117562306a36Sopenharmony_ci * in one go. 117662306a36Sopenharmony_ci * Two cases are possible: 117762306a36Sopenharmony_ci * A) can_queue >= num, 117862306a36Sopenharmony_ci * B) can_queue < num. 117962306a36Sopenharmony_ci * Case A: we can send the whole batch at once. Increment "pending" 118062306a36Sopenharmony_ci * in the beginning of this function, when it is checked, in order to 118162306a36Sopenharmony_ci * eliminate races when this function is called by multiple processes. 118262306a36Sopenharmony_ci * Case B: should never happen. 118362306a36Sopenharmony_ci */ 118462306a36Sopenharmony_ciint asd_post_ascb_list(struct asd_ha_struct *asd_ha, struct asd_ascb *ascb, 118562306a36Sopenharmony_ci int num) 118662306a36Sopenharmony_ci{ 118762306a36Sopenharmony_ci unsigned long flags; 118862306a36Sopenharmony_ci LIST_HEAD(list); 118962306a36Sopenharmony_ci int can_queue; 119062306a36Sopenharmony_ci 119162306a36Sopenharmony_ci spin_lock_irqsave(&asd_ha->seq.pend_q_lock, flags); 119262306a36Sopenharmony_ci can_queue = asd_ha->hw_prof.max_scbs - asd_ha->seq.pending; 119362306a36Sopenharmony_ci if (can_queue >= num) 119462306a36Sopenharmony_ci asd_ha->seq.pending += num; 119562306a36Sopenharmony_ci else 119662306a36Sopenharmony_ci can_queue = 0; 119762306a36Sopenharmony_ci 119862306a36Sopenharmony_ci if (!can_queue) { 119962306a36Sopenharmony_ci spin_unlock_irqrestore(&asd_ha->seq.pend_q_lock, flags); 120062306a36Sopenharmony_ci asd_printk("%s: scb queue full\n", pci_name(asd_ha->pcidev)); 120162306a36Sopenharmony_ci return -SAS_QUEUE_FULL; 120262306a36Sopenharmony_ci } 120362306a36Sopenharmony_ci 120462306a36Sopenharmony_ci asd_swap_head_scb(asd_ha, ascb); 120562306a36Sopenharmony_ci 120662306a36Sopenharmony_ci __list_add(&list, ascb->list.prev, &ascb->list); 120762306a36Sopenharmony_ci 120862306a36Sopenharmony_ci asd_start_scb_timers(&list); 120962306a36Sopenharmony_ci 121062306a36Sopenharmony_ci asd_ha->seq.scbpro += num; 121162306a36Sopenharmony_ci list_splice_init(&list, asd_ha->seq.pend_q.prev); 121262306a36Sopenharmony_ci asd_write_reg_dword(asd_ha, SCBPRO, (u32)asd_ha->seq.scbpro); 121362306a36Sopenharmony_ci spin_unlock_irqrestore(&asd_ha->seq.pend_q_lock, flags); 121462306a36Sopenharmony_ci 121562306a36Sopenharmony_ci return 0; 121662306a36Sopenharmony_ci} 121762306a36Sopenharmony_ci 121862306a36Sopenharmony_ci/** 121962306a36Sopenharmony_ci * asd_post_escb_list -- post a list of 1 or more empty scb 122062306a36Sopenharmony_ci * @asd_ha: pointer to a host adapter structure 122162306a36Sopenharmony_ci * @ascb: pointer to the first empty SCB in the list 122262306a36Sopenharmony_ci * @num: number of aSCBs in the list (to be posted) 122362306a36Sopenharmony_ci * 122462306a36Sopenharmony_ci * This is essentially the same as asd_post_ascb_list, but we do not 122562306a36Sopenharmony_ci * increment pending, add those to the pending list or get indexes. 122662306a36Sopenharmony_ci * See asd_init_escbs() and asd_init_post_escbs(). 122762306a36Sopenharmony_ci * 122862306a36Sopenharmony_ci * Since sending a list of ascbs is a superset of sending a single 122962306a36Sopenharmony_ci * ascb, this function exists to generalize this. More specifically, 123062306a36Sopenharmony_ci * when sending a list of those, we want to do only a _single_ 123162306a36Sopenharmony_ci * memcpy() at swap head, as opposed to for each ascb sent (in the 123262306a36Sopenharmony_ci * case of sending them one by one). That is, we want to minimize the 123362306a36Sopenharmony_ci * ratio of memcpy() operations to the number of ascbs sent. The same 123462306a36Sopenharmony_ci * logic applies to asd_post_ascb_list(). 123562306a36Sopenharmony_ci */ 123662306a36Sopenharmony_ciint asd_post_escb_list(struct asd_ha_struct *asd_ha, struct asd_ascb *ascb, 123762306a36Sopenharmony_ci int num) 123862306a36Sopenharmony_ci{ 123962306a36Sopenharmony_ci unsigned long flags; 124062306a36Sopenharmony_ci 124162306a36Sopenharmony_ci spin_lock_irqsave(&asd_ha->seq.pend_q_lock, flags); 124262306a36Sopenharmony_ci asd_swap_head_scb(asd_ha, ascb); 124362306a36Sopenharmony_ci asd_ha->seq.scbpro += num; 124462306a36Sopenharmony_ci asd_write_reg_dword(asd_ha, SCBPRO, (u32)asd_ha->seq.scbpro); 124562306a36Sopenharmony_ci spin_unlock_irqrestore(&asd_ha->seq.pend_q_lock, flags); 124662306a36Sopenharmony_ci 124762306a36Sopenharmony_ci return 0; 124862306a36Sopenharmony_ci} 124962306a36Sopenharmony_ci 125062306a36Sopenharmony_ci/* ---------- LED ---------- */ 125162306a36Sopenharmony_ci 125262306a36Sopenharmony_ci/** 125362306a36Sopenharmony_ci * asd_turn_led -- turn on/off an LED 125462306a36Sopenharmony_ci * @asd_ha: pointer to host adapter structure 125562306a36Sopenharmony_ci * @phy_id: the PHY id whose LED we want to manupulate 125662306a36Sopenharmony_ci * @op: 1 to turn on, 0 to turn off 125762306a36Sopenharmony_ci */ 125862306a36Sopenharmony_civoid asd_turn_led(struct asd_ha_struct *asd_ha, int phy_id, int op) 125962306a36Sopenharmony_ci{ 126062306a36Sopenharmony_ci if (phy_id < ASD_MAX_PHYS) { 126162306a36Sopenharmony_ci u32 v = asd_read_reg_dword(asd_ha, LmCONTROL(phy_id)); 126262306a36Sopenharmony_ci if (op) 126362306a36Sopenharmony_ci v |= LEDPOL; 126462306a36Sopenharmony_ci else 126562306a36Sopenharmony_ci v &= ~LEDPOL; 126662306a36Sopenharmony_ci asd_write_reg_dword(asd_ha, LmCONTROL(phy_id), v); 126762306a36Sopenharmony_ci } 126862306a36Sopenharmony_ci} 126962306a36Sopenharmony_ci 127062306a36Sopenharmony_ci/** 127162306a36Sopenharmony_ci * asd_control_led -- enable/disable an LED on the board 127262306a36Sopenharmony_ci * @asd_ha: pointer to host adapter structure 127362306a36Sopenharmony_ci * @phy_id: integer, the phy id 127462306a36Sopenharmony_ci * @op: integer, 1 to enable, 0 to disable the LED 127562306a36Sopenharmony_ci * 127662306a36Sopenharmony_ci * First we output enable the LED, then we set the source 127762306a36Sopenharmony_ci * to be an external module. 127862306a36Sopenharmony_ci */ 127962306a36Sopenharmony_civoid asd_control_led(struct asd_ha_struct *asd_ha, int phy_id, int op) 128062306a36Sopenharmony_ci{ 128162306a36Sopenharmony_ci if (phy_id < ASD_MAX_PHYS) { 128262306a36Sopenharmony_ci u32 v; 128362306a36Sopenharmony_ci 128462306a36Sopenharmony_ci v = asd_read_reg_dword(asd_ha, GPIOOER); 128562306a36Sopenharmony_ci if (op) 128662306a36Sopenharmony_ci v |= (1 << phy_id); 128762306a36Sopenharmony_ci else 128862306a36Sopenharmony_ci v &= ~(1 << phy_id); 128962306a36Sopenharmony_ci asd_write_reg_dword(asd_ha, GPIOOER, v); 129062306a36Sopenharmony_ci 129162306a36Sopenharmony_ci v = asd_read_reg_dword(asd_ha, GPIOCNFGR); 129262306a36Sopenharmony_ci if (op) 129362306a36Sopenharmony_ci v |= (1 << phy_id); 129462306a36Sopenharmony_ci else 129562306a36Sopenharmony_ci v &= ~(1 << phy_id); 129662306a36Sopenharmony_ci asd_write_reg_dword(asd_ha, GPIOCNFGR, v); 129762306a36Sopenharmony_ci } 129862306a36Sopenharmony_ci} 129962306a36Sopenharmony_ci 130062306a36Sopenharmony_ci/* ---------- PHY enable ---------- */ 130162306a36Sopenharmony_ci 130262306a36Sopenharmony_cistatic int asd_enable_phy(struct asd_ha_struct *asd_ha, int phy_id) 130362306a36Sopenharmony_ci{ 130462306a36Sopenharmony_ci struct asd_phy *phy = &asd_ha->phys[phy_id]; 130562306a36Sopenharmony_ci 130662306a36Sopenharmony_ci asd_write_reg_byte(asd_ha, LmSEQ_OOB_REG(phy_id, INT_ENABLE_2), 0); 130762306a36Sopenharmony_ci asd_write_reg_byte(asd_ha, LmSEQ_OOB_REG(phy_id, HOT_PLUG_DELAY), 130862306a36Sopenharmony_ci HOTPLUG_DELAY_TIMEOUT); 130962306a36Sopenharmony_ci 131062306a36Sopenharmony_ci /* Get defaults from manuf. sector */ 131162306a36Sopenharmony_ci /* XXX we need defaults for those in case MS is broken. */ 131262306a36Sopenharmony_ci asd_write_reg_byte(asd_ha, LmSEQ_OOB_REG(phy_id, PHY_CONTROL_0), 131362306a36Sopenharmony_ci phy->phy_desc->phy_control_0); 131462306a36Sopenharmony_ci asd_write_reg_byte(asd_ha, LmSEQ_OOB_REG(phy_id, PHY_CONTROL_1), 131562306a36Sopenharmony_ci phy->phy_desc->phy_control_1); 131662306a36Sopenharmony_ci asd_write_reg_byte(asd_ha, LmSEQ_OOB_REG(phy_id, PHY_CONTROL_2), 131762306a36Sopenharmony_ci phy->phy_desc->phy_control_2); 131862306a36Sopenharmony_ci asd_write_reg_byte(asd_ha, LmSEQ_OOB_REG(phy_id, PHY_CONTROL_3), 131962306a36Sopenharmony_ci phy->phy_desc->phy_control_3); 132062306a36Sopenharmony_ci 132162306a36Sopenharmony_ci asd_write_reg_dword(asd_ha, LmSEQ_TEN_MS_COMINIT_TIMEOUT(phy_id), 132262306a36Sopenharmony_ci ASD_COMINIT_TIMEOUT); 132362306a36Sopenharmony_ci 132462306a36Sopenharmony_ci asd_write_reg_addr(asd_ha, LmSEQ_TX_ID_ADDR_FRAME(phy_id), 132562306a36Sopenharmony_ci phy->id_frm_tok->dma_handle); 132662306a36Sopenharmony_ci 132762306a36Sopenharmony_ci asd_control_led(asd_ha, phy_id, 1); 132862306a36Sopenharmony_ci 132962306a36Sopenharmony_ci return 0; 133062306a36Sopenharmony_ci} 133162306a36Sopenharmony_ci 133262306a36Sopenharmony_ciint asd_enable_phys(struct asd_ha_struct *asd_ha, const u8 phy_mask) 133362306a36Sopenharmony_ci{ 133462306a36Sopenharmony_ci u8 phy_m; 133562306a36Sopenharmony_ci u8 i; 133662306a36Sopenharmony_ci int num = 0, k; 133762306a36Sopenharmony_ci struct asd_ascb *ascb; 133862306a36Sopenharmony_ci struct asd_ascb *ascb_list; 133962306a36Sopenharmony_ci 134062306a36Sopenharmony_ci if (!phy_mask) { 134162306a36Sopenharmony_ci asd_printk("%s called with phy_mask of 0!?\n", __func__); 134262306a36Sopenharmony_ci return 0; 134362306a36Sopenharmony_ci } 134462306a36Sopenharmony_ci 134562306a36Sopenharmony_ci for_each_phy(phy_mask, phy_m, i) { 134662306a36Sopenharmony_ci num++; 134762306a36Sopenharmony_ci asd_enable_phy(asd_ha, i); 134862306a36Sopenharmony_ci } 134962306a36Sopenharmony_ci 135062306a36Sopenharmony_ci k = num; 135162306a36Sopenharmony_ci ascb_list = asd_ascb_alloc_list(asd_ha, &k, GFP_KERNEL); 135262306a36Sopenharmony_ci if (!ascb_list) { 135362306a36Sopenharmony_ci asd_printk("no memory for control phy ascb list\n"); 135462306a36Sopenharmony_ci return -ENOMEM; 135562306a36Sopenharmony_ci } 135662306a36Sopenharmony_ci num -= k; 135762306a36Sopenharmony_ci 135862306a36Sopenharmony_ci ascb = ascb_list; 135962306a36Sopenharmony_ci for_each_phy(phy_mask, phy_m, i) { 136062306a36Sopenharmony_ci asd_build_control_phy(ascb, i, ENABLE_PHY); 136162306a36Sopenharmony_ci ascb = list_entry(ascb->list.next, struct asd_ascb, list); 136262306a36Sopenharmony_ci } 136362306a36Sopenharmony_ci ASD_DPRINTK("posting %d control phy scbs\n", num); 136462306a36Sopenharmony_ci k = asd_post_ascb_list(asd_ha, ascb_list, num); 136562306a36Sopenharmony_ci if (k) 136662306a36Sopenharmony_ci asd_ascb_free_list(ascb_list); 136762306a36Sopenharmony_ci 136862306a36Sopenharmony_ci return k; 136962306a36Sopenharmony_ci} 1370