18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Aic94xx SAS/SATA driver hardware interface. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2005 Adaptec, Inc. All rights reserved. 68c2ecf20Sopenharmony_ci * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/pci.h> 108c2ecf20Sopenharmony_ci#include <linux/slab.h> 118c2ecf20Sopenharmony_ci#include <linux/delay.h> 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/firmware.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include "aic94xx.h" 168c2ecf20Sopenharmony_ci#include "aic94xx_reg.h" 178c2ecf20Sopenharmony_ci#include "aic94xx_hwi.h" 188c2ecf20Sopenharmony_ci#include "aic94xx_seq.h" 198c2ecf20Sopenharmony_ci#include "aic94xx_dump.h" 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ciu32 MBAR0_SWB_SIZE; 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci/* ---------- Initialization ---------- */ 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_cistatic int asd_get_user_sas_addr(struct asd_ha_struct *asd_ha) 268c2ecf20Sopenharmony_ci{ 278c2ecf20Sopenharmony_ci /* adapter came with a sas address */ 288c2ecf20Sopenharmony_ci if (asd_ha->hw_prof.sas_addr[0]) 298c2ecf20Sopenharmony_ci return 0; 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci return sas_request_addr(asd_ha->sas_ha.core.shost, 328c2ecf20Sopenharmony_ci asd_ha->hw_prof.sas_addr); 338c2ecf20Sopenharmony_ci} 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistatic void asd_propagate_sas_addr(struct asd_ha_struct *asd_ha) 368c2ecf20Sopenharmony_ci{ 378c2ecf20Sopenharmony_ci int i; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci for (i = 0; i < ASD_MAX_PHYS; i++) { 408c2ecf20Sopenharmony_ci if (asd_ha->hw_prof.phy_desc[i].sas_addr[0] == 0) 418c2ecf20Sopenharmony_ci continue; 428c2ecf20Sopenharmony_ci /* Set a phy's address only if it has none. 438c2ecf20Sopenharmony_ci */ 448c2ecf20Sopenharmony_ci ASD_DPRINTK("setting phy%d addr to %llx\n", i, 458c2ecf20Sopenharmony_ci SAS_ADDR(asd_ha->hw_prof.sas_addr)); 468c2ecf20Sopenharmony_ci memcpy(asd_ha->hw_prof.phy_desc[i].sas_addr, 478c2ecf20Sopenharmony_ci asd_ha->hw_prof.sas_addr, SAS_ADDR_SIZE); 488c2ecf20Sopenharmony_ci } 498c2ecf20Sopenharmony_ci} 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci/* ---------- PHY initialization ---------- */ 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cistatic void asd_init_phy_identify(struct asd_phy *phy) 548c2ecf20Sopenharmony_ci{ 558c2ecf20Sopenharmony_ci phy->identify_frame = phy->id_frm_tok->vaddr; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci memset(phy->identify_frame, 0, sizeof(*phy->identify_frame)); 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci phy->identify_frame->dev_type = SAS_END_DEVICE; 608c2ecf20Sopenharmony_ci if (phy->sas_phy.role & PHY_ROLE_INITIATOR) 618c2ecf20Sopenharmony_ci phy->identify_frame->initiator_bits = phy->sas_phy.iproto; 628c2ecf20Sopenharmony_ci if (phy->sas_phy.role & PHY_ROLE_TARGET) 638c2ecf20Sopenharmony_ci phy->identify_frame->target_bits = phy->sas_phy.tproto; 648c2ecf20Sopenharmony_ci memcpy(phy->identify_frame->sas_addr, phy->phy_desc->sas_addr, 658c2ecf20Sopenharmony_ci SAS_ADDR_SIZE); 668c2ecf20Sopenharmony_ci phy->identify_frame->phy_id = phy->sas_phy.id; 678c2ecf20Sopenharmony_ci} 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_cistatic int asd_init_phy(struct asd_phy *phy) 708c2ecf20Sopenharmony_ci{ 718c2ecf20Sopenharmony_ci struct asd_ha_struct *asd_ha = phy->sas_phy.ha->lldd_ha; 728c2ecf20Sopenharmony_ci struct asd_sas_phy *sas_phy = &phy->sas_phy; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci sas_phy->enabled = 1; 758c2ecf20Sopenharmony_ci sas_phy->class = SAS; 768c2ecf20Sopenharmony_ci sas_phy->iproto = SAS_PROTOCOL_ALL; 778c2ecf20Sopenharmony_ci sas_phy->tproto = 0; 788c2ecf20Sopenharmony_ci sas_phy->type = PHY_TYPE_PHYSICAL; 798c2ecf20Sopenharmony_ci sas_phy->role = PHY_ROLE_INITIATOR; 808c2ecf20Sopenharmony_ci sas_phy->oob_mode = OOB_NOT_CONNECTED; 818c2ecf20Sopenharmony_ci sas_phy->linkrate = SAS_LINK_RATE_UNKNOWN; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci phy->id_frm_tok = asd_alloc_coherent(asd_ha, 848c2ecf20Sopenharmony_ci sizeof(*phy->identify_frame), 858c2ecf20Sopenharmony_ci GFP_KERNEL); 868c2ecf20Sopenharmony_ci if (!phy->id_frm_tok) { 878c2ecf20Sopenharmony_ci asd_printk("no mem for IDENTIFY for phy%d\n", sas_phy->id); 888c2ecf20Sopenharmony_ci return -ENOMEM; 898c2ecf20Sopenharmony_ci } else 908c2ecf20Sopenharmony_ci asd_init_phy_identify(phy); 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci memset(phy->frame_rcvd, 0, sizeof(phy->frame_rcvd)); 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci return 0; 958c2ecf20Sopenharmony_ci} 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_cistatic void asd_init_ports(struct asd_ha_struct *asd_ha) 988c2ecf20Sopenharmony_ci{ 998c2ecf20Sopenharmony_ci int i; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci spin_lock_init(&asd_ha->asd_ports_lock); 1028c2ecf20Sopenharmony_ci for (i = 0; i < ASD_MAX_PHYS; i++) { 1038c2ecf20Sopenharmony_ci struct asd_port *asd_port = &asd_ha->asd_ports[i]; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci memset(asd_port->sas_addr, 0, SAS_ADDR_SIZE); 1068c2ecf20Sopenharmony_ci memset(asd_port->attached_sas_addr, 0, SAS_ADDR_SIZE); 1078c2ecf20Sopenharmony_ci asd_port->phy_mask = 0; 1088c2ecf20Sopenharmony_ci asd_port->num_phys = 0; 1098c2ecf20Sopenharmony_ci } 1108c2ecf20Sopenharmony_ci} 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_cistatic int asd_init_phys(struct asd_ha_struct *asd_ha) 1138c2ecf20Sopenharmony_ci{ 1148c2ecf20Sopenharmony_ci u8 i; 1158c2ecf20Sopenharmony_ci u8 phy_mask = asd_ha->hw_prof.enabled_phys; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci for (i = 0; i < ASD_MAX_PHYS; i++) { 1188c2ecf20Sopenharmony_ci struct asd_phy *phy = &asd_ha->phys[i]; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci phy->phy_desc = &asd_ha->hw_prof.phy_desc[i]; 1218c2ecf20Sopenharmony_ci phy->asd_port = NULL; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci phy->sas_phy.enabled = 0; 1248c2ecf20Sopenharmony_ci phy->sas_phy.id = i; 1258c2ecf20Sopenharmony_ci phy->sas_phy.sas_addr = &phy->phy_desc->sas_addr[0]; 1268c2ecf20Sopenharmony_ci phy->sas_phy.frame_rcvd = &phy->frame_rcvd[0]; 1278c2ecf20Sopenharmony_ci phy->sas_phy.ha = &asd_ha->sas_ha; 1288c2ecf20Sopenharmony_ci phy->sas_phy.lldd_phy = phy; 1298c2ecf20Sopenharmony_ci } 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci /* Now enable and initialize only the enabled phys. */ 1328c2ecf20Sopenharmony_ci for_each_phy(phy_mask, phy_mask, i) { 1338c2ecf20Sopenharmony_ci int err = asd_init_phy(&asd_ha->phys[i]); 1348c2ecf20Sopenharmony_ci if (err) 1358c2ecf20Sopenharmony_ci return err; 1368c2ecf20Sopenharmony_ci } 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci return 0; 1398c2ecf20Sopenharmony_ci} 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci/* ---------- Sliding windows ---------- */ 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_cistatic int asd_init_sw(struct asd_ha_struct *asd_ha) 1448c2ecf20Sopenharmony_ci{ 1458c2ecf20Sopenharmony_ci struct pci_dev *pcidev = asd_ha->pcidev; 1468c2ecf20Sopenharmony_ci int err; 1478c2ecf20Sopenharmony_ci u32 v; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci /* Unlock MBARs */ 1508c2ecf20Sopenharmony_ci err = pci_read_config_dword(pcidev, PCI_CONF_MBAR_KEY, &v); 1518c2ecf20Sopenharmony_ci if (err) { 1528c2ecf20Sopenharmony_ci asd_printk("couldn't access conf. space of %s\n", 1538c2ecf20Sopenharmony_ci pci_name(pcidev)); 1548c2ecf20Sopenharmony_ci goto Err; 1558c2ecf20Sopenharmony_ci } 1568c2ecf20Sopenharmony_ci if (v) 1578c2ecf20Sopenharmony_ci err = pci_write_config_dword(pcidev, PCI_CONF_MBAR_KEY, v); 1588c2ecf20Sopenharmony_ci if (err) { 1598c2ecf20Sopenharmony_ci asd_printk("couldn't write to MBAR_KEY of %s\n", 1608c2ecf20Sopenharmony_ci pci_name(pcidev)); 1618c2ecf20Sopenharmony_ci goto Err; 1628c2ecf20Sopenharmony_ci } 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci /* Set sliding windows A, B and C to point to proper internal 1658c2ecf20Sopenharmony_ci * memory regions. 1668c2ecf20Sopenharmony_ci */ 1678c2ecf20Sopenharmony_ci pci_write_config_dword(pcidev, PCI_CONF_MBAR0_SWA, REG_BASE_ADDR); 1688c2ecf20Sopenharmony_ci pci_write_config_dword(pcidev, PCI_CONF_MBAR0_SWB, 1698c2ecf20Sopenharmony_ci REG_BASE_ADDR_CSEQCIO); 1708c2ecf20Sopenharmony_ci pci_write_config_dword(pcidev, PCI_CONF_MBAR0_SWC, REG_BASE_ADDR_EXSI); 1718c2ecf20Sopenharmony_ci asd_ha->io_handle[0].swa_base = REG_BASE_ADDR; 1728c2ecf20Sopenharmony_ci asd_ha->io_handle[0].swb_base = REG_BASE_ADDR_CSEQCIO; 1738c2ecf20Sopenharmony_ci asd_ha->io_handle[0].swc_base = REG_BASE_ADDR_EXSI; 1748c2ecf20Sopenharmony_ci MBAR0_SWB_SIZE = asd_ha->io_handle[0].len - 0x80; 1758c2ecf20Sopenharmony_ci if (!asd_ha->iospace) { 1768c2ecf20Sopenharmony_ci /* MBAR1 will point to OCM (On Chip Memory) */ 1778c2ecf20Sopenharmony_ci pci_write_config_dword(pcidev, PCI_CONF_MBAR1, OCM_BASE_ADDR); 1788c2ecf20Sopenharmony_ci asd_ha->io_handle[1].swa_base = OCM_BASE_ADDR; 1798c2ecf20Sopenharmony_ci } 1808c2ecf20Sopenharmony_ci spin_lock_init(&asd_ha->iolock); 1818c2ecf20Sopenharmony_ciErr: 1828c2ecf20Sopenharmony_ci return err; 1838c2ecf20Sopenharmony_ci} 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci/* ---------- SCB initialization ---------- */ 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci/** 1888c2ecf20Sopenharmony_ci * asd_init_scbs - manually allocate the first SCB. 1898c2ecf20Sopenharmony_ci * @asd_ha: pointer to host adapter structure 1908c2ecf20Sopenharmony_ci * 1918c2ecf20Sopenharmony_ci * This allocates the very first SCB which would be sent to the 1928c2ecf20Sopenharmony_ci * sequencer for execution. Its bus address is written to 1938c2ecf20Sopenharmony_ci * CSEQ_Q_NEW_POINTER, mode page 2, mode 8. Since the bus address of 1948c2ecf20Sopenharmony_ci * the _next_ scb to be DMA-ed to the host adapter is read from the last 1958c2ecf20Sopenharmony_ci * SCB DMA-ed to the host adapter, we have to always stay one step 1968c2ecf20Sopenharmony_ci * ahead of the sequencer and keep one SCB already allocated. 1978c2ecf20Sopenharmony_ci */ 1988c2ecf20Sopenharmony_cistatic int asd_init_scbs(struct asd_ha_struct *asd_ha) 1998c2ecf20Sopenharmony_ci{ 2008c2ecf20Sopenharmony_ci struct asd_seq_data *seq = &asd_ha->seq; 2018c2ecf20Sopenharmony_ci int bitmap_bytes; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci /* allocate the index array and bitmap */ 2048c2ecf20Sopenharmony_ci asd_ha->seq.tc_index_bitmap_bits = asd_ha->hw_prof.max_scbs; 2058c2ecf20Sopenharmony_ci asd_ha->seq.tc_index_array = kcalloc(asd_ha->seq.tc_index_bitmap_bits, 2068c2ecf20Sopenharmony_ci sizeof(void *), 2078c2ecf20Sopenharmony_ci GFP_KERNEL); 2088c2ecf20Sopenharmony_ci if (!asd_ha->seq.tc_index_array) 2098c2ecf20Sopenharmony_ci return -ENOMEM; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci bitmap_bytes = (asd_ha->seq.tc_index_bitmap_bits+7)/8; 2128c2ecf20Sopenharmony_ci bitmap_bytes = BITS_TO_LONGS(bitmap_bytes*8)*sizeof(unsigned long); 2138c2ecf20Sopenharmony_ci asd_ha->seq.tc_index_bitmap = kzalloc(bitmap_bytes, GFP_KERNEL); 2148c2ecf20Sopenharmony_ci if (!asd_ha->seq.tc_index_bitmap) { 2158c2ecf20Sopenharmony_ci kfree(asd_ha->seq.tc_index_array); 2168c2ecf20Sopenharmony_ci asd_ha->seq.tc_index_array = NULL; 2178c2ecf20Sopenharmony_ci return -ENOMEM; 2188c2ecf20Sopenharmony_ci } 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci spin_lock_init(&seq->tc_index_lock); 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci seq->next_scb.size = sizeof(struct scb); 2238c2ecf20Sopenharmony_ci seq->next_scb.vaddr = dma_pool_alloc(asd_ha->scb_pool, GFP_KERNEL, 2248c2ecf20Sopenharmony_ci &seq->next_scb.dma_handle); 2258c2ecf20Sopenharmony_ci if (!seq->next_scb.vaddr) { 2268c2ecf20Sopenharmony_ci kfree(asd_ha->seq.tc_index_bitmap); 2278c2ecf20Sopenharmony_ci kfree(asd_ha->seq.tc_index_array); 2288c2ecf20Sopenharmony_ci asd_ha->seq.tc_index_bitmap = NULL; 2298c2ecf20Sopenharmony_ci asd_ha->seq.tc_index_array = NULL; 2308c2ecf20Sopenharmony_ci return -ENOMEM; 2318c2ecf20Sopenharmony_ci } 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci seq->pending = 0; 2348c2ecf20Sopenharmony_ci spin_lock_init(&seq->pend_q_lock); 2358c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&seq->pend_q); 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci return 0; 2388c2ecf20Sopenharmony_ci} 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_cistatic void asd_get_max_scb_ddb(struct asd_ha_struct *asd_ha) 2418c2ecf20Sopenharmony_ci{ 2428c2ecf20Sopenharmony_ci asd_ha->hw_prof.max_scbs = asd_get_cmdctx_size(asd_ha)/ASD_SCB_SIZE; 2438c2ecf20Sopenharmony_ci asd_ha->hw_prof.max_ddbs = asd_get_devctx_size(asd_ha)/ASD_DDB_SIZE; 2448c2ecf20Sopenharmony_ci ASD_DPRINTK("max_scbs:%d, max_ddbs:%d\n", 2458c2ecf20Sopenharmony_ci asd_ha->hw_prof.max_scbs, 2468c2ecf20Sopenharmony_ci asd_ha->hw_prof.max_ddbs); 2478c2ecf20Sopenharmony_ci} 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci/* ---------- Done List initialization ---------- */ 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_cistatic void asd_dl_tasklet_handler(unsigned long); 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_cistatic int asd_init_dl(struct asd_ha_struct *asd_ha) 2548c2ecf20Sopenharmony_ci{ 2558c2ecf20Sopenharmony_ci asd_ha->seq.actual_dl 2568c2ecf20Sopenharmony_ci = asd_alloc_coherent(asd_ha, 2578c2ecf20Sopenharmony_ci ASD_DL_SIZE * sizeof(struct done_list_struct), 2588c2ecf20Sopenharmony_ci GFP_KERNEL); 2598c2ecf20Sopenharmony_ci if (!asd_ha->seq.actual_dl) 2608c2ecf20Sopenharmony_ci return -ENOMEM; 2618c2ecf20Sopenharmony_ci asd_ha->seq.dl = asd_ha->seq.actual_dl->vaddr; 2628c2ecf20Sopenharmony_ci asd_ha->seq.dl_toggle = ASD_DEF_DL_TOGGLE; 2638c2ecf20Sopenharmony_ci asd_ha->seq.dl_next = 0; 2648c2ecf20Sopenharmony_ci tasklet_init(&asd_ha->seq.dl_tasklet, asd_dl_tasklet_handler, 2658c2ecf20Sopenharmony_ci (unsigned long) asd_ha); 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci return 0; 2688c2ecf20Sopenharmony_ci} 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci/* ---------- EDB and ESCB init ---------- */ 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_cistatic int asd_alloc_edbs(struct asd_ha_struct *asd_ha, gfp_t gfp_flags) 2738c2ecf20Sopenharmony_ci{ 2748c2ecf20Sopenharmony_ci struct asd_seq_data *seq = &asd_ha->seq; 2758c2ecf20Sopenharmony_ci int i; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci seq->edb_arr = kmalloc_array(seq->num_edbs, sizeof(*seq->edb_arr), 2788c2ecf20Sopenharmony_ci gfp_flags); 2798c2ecf20Sopenharmony_ci if (!seq->edb_arr) 2808c2ecf20Sopenharmony_ci return -ENOMEM; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci for (i = 0; i < seq->num_edbs; i++) { 2838c2ecf20Sopenharmony_ci seq->edb_arr[i] = asd_alloc_coherent(asd_ha, ASD_EDB_SIZE, 2848c2ecf20Sopenharmony_ci gfp_flags); 2858c2ecf20Sopenharmony_ci if (!seq->edb_arr[i]) 2868c2ecf20Sopenharmony_ci goto Err_unroll; 2878c2ecf20Sopenharmony_ci memset(seq->edb_arr[i]->vaddr, 0, ASD_EDB_SIZE); 2888c2ecf20Sopenharmony_ci } 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci ASD_DPRINTK("num_edbs:%d\n", seq->num_edbs); 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci return 0; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ciErr_unroll: 2958c2ecf20Sopenharmony_ci for (i-- ; i >= 0; i--) 2968c2ecf20Sopenharmony_ci asd_free_coherent(asd_ha, seq->edb_arr[i]); 2978c2ecf20Sopenharmony_ci kfree(seq->edb_arr); 2988c2ecf20Sopenharmony_ci seq->edb_arr = NULL; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci return -ENOMEM; 3018c2ecf20Sopenharmony_ci} 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_cistatic int asd_alloc_escbs(struct asd_ha_struct *asd_ha, 3048c2ecf20Sopenharmony_ci gfp_t gfp_flags) 3058c2ecf20Sopenharmony_ci{ 3068c2ecf20Sopenharmony_ci struct asd_seq_data *seq = &asd_ha->seq; 3078c2ecf20Sopenharmony_ci struct asd_ascb *escb; 3088c2ecf20Sopenharmony_ci int i, escbs; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci seq->escb_arr = kmalloc_array(seq->num_escbs, sizeof(*seq->escb_arr), 3118c2ecf20Sopenharmony_ci gfp_flags); 3128c2ecf20Sopenharmony_ci if (!seq->escb_arr) 3138c2ecf20Sopenharmony_ci return -ENOMEM; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci escbs = seq->num_escbs; 3168c2ecf20Sopenharmony_ci escb = asd_ascb_alloc_list(asd_ha, &escbs, gfp_flags); 3178c2ecf20Sopenharmony_ci if (!escb) { 3188c2ecf20Sopenharmony_ci asd_printk("couldn't allocate list of escbs\n"); 3198c2ecf20Sopenharmony_ci goto Err; 3208c2ecf20Sopenharmony_ci } 3218c2ecf20Sopenharmony_ci seq->num_escbs -= escbs; /* subtract what was not allocated */ 3228c2ecf20Sopenharmony_ci ASD_DPRINTK("num_escbs:%d\n", seq->num_escbs); 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci for (i = 0; i < seq->num_escbs; i++, escb = list_entry(escb->list.next, 3258c2ecf20Sopenharmony_ci struct asd_ascb, 3268c2ecf20Sopenharmony_ci list)) { 3278c2ecf20Sopenharmony_ci seq->escb_arr[i] = escb; 3288c2ecf20Sopenharmony_ci escb->scb->header.opcode = EMPTY_SCB; 3298c2ecf20Sopenharmony_ci } 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci return 0; 3328c2ecf20Sopenharmony_ciErr: 3338c2ecf20Sopenharmony_ci kfree(seq->escb_arr); 3348c2ecf20Sopenharmony_ci seq->escb_arr = NULL; 3358c2ecf20Sopenharmony_ci return -ENOMEM; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci} 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_cistatic void asd_assign_edbs2escbs(struct asd_ha_struct *asd_ha) 3408c2ecf20Sopenharmony_ci{ 3418c2ecf20Sopenharmony_ci struct asd_seq_data *seq = &asd_ha->seq; 3428c2ecf20Sopenharmony_ci int i, k, z = 0; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci for (i = 0; i < seq->num_escbs; i++) { 3458c2ecf20Sopenharmony_ci struct asd_ascb *ascb = seq->escb_arr[i]; 3468c2ecf20Sopenharmony_ci struct empty_scb *escb = &ascb->scb->escb; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci ascb->edb_index = z; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci escb->num_valid = ASD_EDBS_PER_SCB; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci for (k = 0; k < ASD_EDBS_PER_SCB; k++) { 3538c2ecf20Sopenharmony_ci struct sg_el *eb = &escb->eb[k]; 3548c2ecf20Sopenharmony_ci struct asd_dma_tok *edb = seq->edb_arr[z++]; 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci memset(eb, 0, sizeof(*eb)); 3578c2ecf20Sopenharmony_ci eb->bus_addr = cpu_to_le64(((u64) edb->dma_handle)); 3588c2ecf20Sopenharmony_ci eb->size = cpu_to_le32(((u32) edb->size)); 3598c2ecf20Sopenharmony_ci } 3608c2ecf20Sopenharmony_ci } 3618c2ecf20Sopenharmony_ci} 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci/** 3648c2ecf20Sopenharmony_ci * asd_init_escbs -- allocate and initialize empty scbs 3658c2ecf20Sopenharmony_ci * @asd_ha: pointer to host adapter structure 3668c2ecf20Sopenharmony_ci * 3678c2ecf20Sopenharmony_ci * An empty SCB has sg_elements of ASD_EDBS_PER_SCB (7) buffers. 3688c2ecf20Sopenharmony_ci * They transport sense data, etc. 3698c2ecf20Sopenharmony_ci */ 3708c2ecf20Sopenharmony_cistatic int asd_init_escbs(struct asd_ha_struct *asd_ha) 3718c2ecf20Sopenharmony_ci{ 3728c2ecf20Sopenharmony_ci struct asd_seq_data *seq = &asd_ha->seq; 3738c2ecf20Sopenharmony_ci int err = 0; 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci /* Allocate two empty data buffers (edb) per sequencer. */ 3768c2ecf20Sopenharmony_ci int edbs = 2*(1+asd_ha->hw_prof.num_phys); 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci seq->num_escbs = (edbs+ASD_EDBS_PER_SCB-1)/ASD_EDBS_PER_SCB; 3798c2ecf20Sopenharmony_ci seq->num_edbs = seq->num_escbs * ASD_EDBS_PER_SCB; 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci err = asd_alloc_edbs(asd_ha, GFP_KERNEL); 3828c2ecf20Sopenharmony_ci if (err) { 3838c2ecf20Sopenharmony_ci asd_printk("couldn't allocate edbs\n"); 3848c2ecf20Sopenharmony_ci return err; 3858c2ecf20Sopenharmony_ci } 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci err = asd_alloc_escbs(asd_ha, GFP_KERNEL); 3888c2ecf20Sopenharmony_ci if (err) { 3898c2ecf20Sopenharmony_ci asd_printk("couldn't allocate escbs\n"); 3908c2ecf20Sopenharmony_ci return err; 3918c2ecf20Sopenharmony_ci } 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci asd_assign_edbs2escbs(asd_ha); 3948c2ecf20Sopenharmony_ci /* In order to insure that normal SCBs do not overfill sequencer 3958c2ecf20Sopenharmony_ci * memory and leave no space for escbs (halting condition), 3968c2ecf20Sopenharmony_ci * we increment pending here by the number of escbs. However, 3978c2ecf20Sopenharmony_ci * escbs are never pending. 3988c2ecf20Sopenharmony_ci */ 3998c2ecf20Sopenharmony_ci seq->pending = seq->num_escbs; 4008c2ecf20Sopenharmony_ci seq->can_queue = 1 + (asd_ha->hw_prof.max_scbs - seq->pending)/2; 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci return 0; 4038c2ecf20Sopenharmony_ci} 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci/* ---------- HW initialization ---------- */ 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci/** 4088c2ecf20Sopenharmony_ci * asd_chip_hardrst -- hard reset the chip 4098c2ecf20Sopenharmony_ci * @asd_ha: pointer to host adapter structure 4108c2ecf20Sopenharmony_ci * 4118c2ecf20Sopenharmony_ci * This takes 16 cycles and is synchronous to CFCLK, which runs 4128c2ecf20Sopenharmony_ci * at 200 MHz, so this should take at most 80 nanoseconds. 4138c2ecf20Sopenharmony_ci */ 4148c2ecf20Sopenharmony_ciint asd_chip_hardrst(struct asd_ha_struct *asd_ha) 4158c2ecf20Sopenharmony_ci{ 4168c2ecf20Sopenharmony_ci int i; 4178c2ecf20Sopenharmony_ci int count = 100; 4188c2ecf20Sopenharmony_ci u32 reg; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci for (i = 0 ; i < 4 ; i++) { 4218c2ecf20Sopenharmony_ci asd_write_reg_dword(asd_ha, COMBIST, HARDRST); 4228c2ecf20Sopenharmony_ci } 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci do { 4258c2ecf20Sopenharmony_ci udelay(1); 4268c2ecf20Sopenharmony_ci reg = asd_read_reg_dword(asd_ha, CHIMINT); 4278c2ecf20Sopenharmony_ci if (reg & HARDRSTDET) { 4288c2ecf20Sopenharmony_ci asd_write_reg_dword(asd_ha, CHIMINT, 4298c2ecf20Sopenharmony_ci HARDRSTDET|PORRSTDET); 4308c2ecf20Sopenharmony_ci return 0; 4318c2ecf20Sopenharmony_ci } 4328c2ecf20Sopenharmony_ci } while (--count > 0); 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci return -ENODEV; 4358c2ecf20Sopenharmony_ci} 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci/** 4388c2ecf20Sopenharmony_ci * asd_init_chip -- initialize the chip 4398c2ecf20Sopenharmony_ci * @asd_ha: pointer to host adapter structure 4408c2ecf20Sopenharmony_ci * 4418c2ecf20Sopenharmony_ci * Hard resets the chip, disables HA interrupts, downloads the sequnecer 4428c2ecf20Sopenharmony_ci * microcode and starts the sequencers. The caller has to explicitly 4438c2ecf20Sopenharmony_ci * enable HA interrupts with asd_enable_ints(asd_ha). 4448c2ecf20Sopenharmony_ci */ 4458c2ecf20Sopenharmony_cistatic int asd_init_chip(struct asd_ha_struct *asd_ha) 4468c2ecf20Sopenharmony_ci{ 4478c2ecf20Sopenharmony_ci int err; 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci err = asd_chip_hardrst(asd_ha); 4508c2ecf20Sopenharmony_ci if (err) { 4518c2ecf20Sopenharmony_ci asd_printk("couldn't hard reset %s\n", 4528c2ecf20Sopenharmony_ci pci_name(asd_ha->pcidev)); 4538c2ecf20Sopenharmony_ci goto out; 4548c2ecf20Sopenharmony_ci } 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci asd_disable_ints(asd_ha); 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci err = asd_init_seqs(asd_ha); 4598c2ecf20Sopenharmony_ci if (err) { 4608c2ecf20Sopenharmony_ci asd_printk("couldn't init seqs for %s\n", 4618c2ecf20Sopenharmony_ci pci_name(asd_ha->pcidev)); 4628c2ecf20Sopenharmony_ci goto out; 4638c2ecf20Sopenharmony_ci } 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci err = asd_start_seqs(asd_ha); 4668c2ecf20Sopenharmony_ci if (err) { 4678c2ecf20Sopenharmony_ci asd_printk("couldn't start seqs for %s\n", 4688c2ecf20Sopenharmony_ci pci_name(asd_ha->pcidev)); 4698c2ecf20Sopenharmony_ci goto out; 4708c2ecf20Sopenharmony_ci } 4718c2ecf20Sopenharmony_ciout: 4728c2ecf20Sopenharmony_ci return err; 4738c2ecf20Sopenharmony_ci} 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci#define MAX_DEVS ((OCM_MAX_SIZE) / (ASD_DDB_SIZE)) 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_cistatic int max_devs = 0; 4788c2ecf20Sopenharmony_cimodule_param_named(max_devs, max_devs, int, S_IRUGO); 4798c2ecf20Sopenharmony_ciMODULE_PARM_DESC(max_devs, "\n" 4808c2ecf20Sopenharmony_ci "\tMaximum number of SAS devices to support (not LUs).\n" 4818c2ecf20Sopenharmony_ci "\tDefault: 2176, Maximum: 65663.\n"); 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_cistatic int max_cmnds = 0; 4848c2ecf20Sopenharmony_cimodule_param_named(max_cmnds, max_cmnds, int, S_IRUGO); 4858c2ecf20Sopenharmony_ciMODULE_PARM_DESC(max_cmnds, "\n" 4868c2ecf20Sopenharmony_ci "\tMaximum number of commands queuable.\n" 4878c2ecf20Sopenharmony_ci "\tDefault: 512, Maximum: 66047.\n"); 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_cistatic void asd_extend_devctx_ocm(struct asd_ha_struct *asd_ha) 4908c2ecf20Sopenharmony_ci{ 4918c2ecf20Sopenharmony_ci unsigned long dma_addr = OCM_BASE_ADDR; 4928c2ecf20Sopenharmony_ci u32 d; 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci dma_addr -= asd_ha->hw_prof.max_ddbs * ASD_DDB_SIZE; 4958c2ecf20Sopenharmony_ci asd_write_reg_addr(asd_ha, DEVCTXBASE, (dma_addr_t) dma_addr); 4968c2ecf20Sopenharmony_ci d = asd_read_reg_dword(asd_ha, CTXDOMAIN); 4978c2ecf20Sopenharmony_ci d |= 4; 4988c2ecf20Sopenharmony_ci asd_write_reg_dword(asd_ha, CTXDOMAIN, d); 4998c2ecf20Sopenharmony_ci asd_ha->hw_prof.max_ddbs += MAX_DEVS; 5008c2ecf20Sopenharmony_ci} 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_cistatic int asd_extend_devctx(struct asd_ha_struct *asd_ha) 5038c2ecf20Sopenharmony_ci{ 5048c2ecf20Sopenharmony_ci dma_addr_t dma_handle; 5058c2ecf20Sopenharmony_ci unsigned long dma_addr; 5068c2ecf20Sopenharmony_ci u32 d; 5078c2ecf20Sopenharmony_ci int size; 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci asd_extend_devctx_ocm(asd_ha); 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci asd_ha->hw_prof.ddb_ext = NULL; 5128c2ecf20Sopenharmony_ci if (max_devs <= asd_ha->hw_prof.max_ddbs || max_devs > 0xFFFF) { 5138c2ecf20Sopenharmony_ci max_devs = asd_ha->hw_prof.max_ddbs; 5148c2ecf20Sopenharmony_ci return 0; 5158c2ecf20Sopenharmony_ci } 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci size = (max_devs - asd_ha->hw_prof.max_ddbs + 1) * ASD_DDB_SIZE; 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci asd_ha->hw_prof.ddb_ext = asd_alloc_coherent(asd_ha, size, GFP_KERNEL); 5208c2ecf20Sopenharmony_ci if (!asd_ha->hw_prof.ddb_ext) { 5218c2ecf20Sopenharmony_ci asd_printk("couldn't allocate memory for %d devices\n", 5228c2ecf20Sopenharmony_ci max_devs); 5238c2ecf20Sopenharmony_ci max_devs = asd_ha->hw_prof.max_ddbs; 5248c2ecf20Sopenharmony_ci return -ENOMEM; 5258c2ecf20Sopenharmony_ci } 5268c2ecf20Sopenharmony_ci dma_handle = asd_ha->hw_prof.ddb_ext->dma_handle; 5278c2ecf20Sopenharmony_ci dma_addr = ALIGN((unsigned long) dma_handle, ASD_DDB_SIZE); 5288c2ecf20Sopenharmony_ci dma_addr -= asd_ha->hw_prof.max_ddbs * ASD_DDB_SIZE; 5298c2ecf20Sopenharmony_ci dma_handle = (dma_addr_t) dma_addr; 5308c2ecf20Sopenharmony_ci asd_write_reg_addr(asd_ha, DEVCTXBASE, dma_handle); 5318c2ecf20Sopenharmony_ci d = asd_read_reg_dword(asd_ha, CTXDOMAIN); 5328c2ecf20Sopenharmony_ci d &= ~4; 5338c2ecf20Sopenharmony_ci asd_write_reg_dword(asd_ha, CTXDOMAIN, d); 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci asd_ha->hw_prof.max_ddbs = max_devs; 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci return 0; 5388c2ecf20Sopenharmony_ci} 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_cistatic int asd_extend_cmdctx(struct asd_ha_struct *asd_ha) 5418c2ecf20Sopenharmony_ci{ 5428c2ecf20Sopenharmony_ci dma_addr_t dma_handle; 5438c2ecf20Sopenharmony_ci unsigned long dma_addr; 5448c2ecf20Sopenharmony_ci u32 d; 5458c2ecf20Sopenharmony_ci int size; 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci asd_ha->hw_prof.scb_ext = NULL; 5488c2ecf20Sopenharmony_ci if (max_cmnds <= asd_ha->hw_prof.max_scbs || max_cmnds > 0xFFFF) { 5498c2ecf20Sopenharmony_ci max_cmnds = asd_ha->hw_prof.max_scbs; 5508c2ecf20Sopenharmony_ci return 0; 5518c2ecf20Sopenharmony_ci } 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci size = (max_cmnds - asd_ha->hw_prof.max_scbs + 1) * ASD_SCB_SIZE; 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci asd_ha->hw_prof.scb_ext = asd_alloc_coherent(asd_ha, size, GFP_KERNEL); 5568c2ecf20Sopenharmony_ci if (!asd_ha->hw_prof.scb_ext) { 5578c2ecf20Sopenharmony_ci asd_printk("couldn't allocate memory for %d commands\n", 5588c2ecf20Sopenharmony_ci max_cmnds); 5598c2ecf20Sopenharmony_ci max_cmnds = asd_ha->hw_prof.max_scbs; 5608c2ecf20Sopenharmony_ci return -ENOMEM; 5618c2ecf20Sopenharmony_ci } 5628c2ecf20Sopenharmony_ci dma_handle = asd_ha->hw_prof.scb_ext->dma_handle; 5638c2ecf20Sopenharmony_ci dma_addr = ALIGN((unsigned long) dma_handle, ASD_SCB_SIZE); 5648c2ecf20Sopenharmony_ci dma_addr -= asd_ha->hw_prof.max_scbs * ASD_SCB_SIZE; 5658c2ecf20Sopenharmony_ci dma_handle = (dma_addr_t) dma_addr; 5668c2ecf20Sopenharmony_ci asd_write_reg_addr(asd_ha, CMDCTXBASE, dma_handle); 5678c2ecf20Sopenharmony_ci d = asd_read_reg_dword(asd_ha, CTXDOMAIN); 5688c2ecf20Sopenharmony_ci d &= ~1; 5698c2ecf20Sopenharmony_ci asd_write_reg_dword(asd_ha, CTXDOMAIN, d); 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci asd_ha->hw_prof.max_scbs = max_cmnds; 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci return 0; 5748c2ecf20Sopenharmony_ci} 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci/** 5778c2ecf20Sopenharmony_ci * asd_init_ctxmem -- initialize context memory 5788c2ecf20Sopenharmony_ci * @asd_ha: pointer to host adapter structure 5798c2ecf20Sopenharmony_ci * 5808c2ecf20Sopenharmony_ci * This function sets the maximum number of SCBs and 5818c2ecf20Sopenharmony_ci * DDBs which can be used by the sequencer. This is normally 5828c2ecf20Sopenharmony_ci * 512 and 128 respectively. If support for more SCBs or more DDBs 5838c2ecf20Sopenharmony_ci * is required then CMDCTXBASE, DEVCTXBASE and CTXDOMAIN are 5848c2ecf20Sopenharmony_ci * initialized here to extend context memory to point to host memory, 5858c2ecf20Sopenharmony_ci * thus allowing unlimited support for SCBs and DDBs -- only limited 5868c2ecf20Sopenharmony_ci * by host memory. 5878c2ecf20Sopenharmony_ci */ 5888c2ecf20Sopenharmony_cistatic int asd_init_ctxmem(struct asd_ha_struct *asd_ha) 5898c2ecf20Sopenharmony_ci{ 5908c2ecf20Sopenharmony_ci int bitmap_bytes; 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci asd_get_max_scb_ddb(asd_ha); 5938c2ecf20Sopenharmony_ci asd_extend_devctx(asd_ha); 5948c2ecf20Sopenharmony_ci asd_extend_cmdctx(asd_ha); 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci /* The kernel wants bitmaps to be unsigned long sized. */ 5978c2ecf20Sopenharmony_ci bitmap_bytes = (asd_ha->hw_prof.max_ddbs+7)/8; 5988c2ecf20Sopenharmony_ci bitmap_bytes = BITS_TO_LONGS(bitmap_bytes*8)*sizeof(unsigned long); 5998c2ecf20Sopenharmony_ci asd_ha->hw_prof.ddb_bitmap = kzalloc(bitmap_bytes, GFP_KERNEL); 6008c2ecf20Sopenharmony_ci if (!asd_ha->hw_prof.ddb_bitmap) 6018c2ecf20Sopenharmony_ci return -ENOMEM; 6028c2ecf20Sopenharmony_ci spin_lock_init(&asd_ha->hw_prof.ddb_lock); 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci return 0; 6058c2ecf20Sopenharmony_ci} 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ciint asd_init_hw(struct asd_ha_struct *asd_ha) 6088c2ecf20Sopenharmony_ci{ 6098c2ecf20Sopenharmony_ci int err; 6108c2ecf20Sopenharmony_ci u32 v; 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci err = asd_init_sw(asd_ha); 6138c2ecf20Sopenharmony_ci if (err) 6148c2ecf20Sopenharmony_ci return err; 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci err = pci_read_config_dword(asd_ha->pcidev, PCIC_HSTPCIX_CNTRL, &v); 6178c2ecf20Sopenharmony_ci if (err) { 6188c2ecf20Sopenharmony_ci asd_printk("couldn't read PCIC_HSTPCIX_CNTRL of %s\n", 6198c2ecf20Sopenharmony_ci pci_name(asd_ha->pcidev)); 6208c2ecf20Sopenharmony_ci return err; 6218c2ecf20Sopenharmony_ci } 6228c2ecf20Sopenharmony_ci err = pci_write_config_dword(asd_ha->pcidev, PCIC_HSTPCIX_CNTRL, 6238c2ecf20Sopenharmony_ci v | SC_TMR_DIS); 6248c2ecf20Sopenharmony_ci if (err) { 6258c2ecf20Sopenharmony_ci asd_printk("couldn't disable split completion timer of %s\n", 6268c2ecf20Sopenharmony_ci pci_name(asd_ha->pcidev)); 6278c2ecf20Sopenharmony_ci return err; 6288c2ecf20Sopenharmony_ci } 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci err = asd_read_ocm(asd_ha); 6318c2ecf20Sopenharmony_ci if (err) { 6328c2ecf20Sopenharmony_ci asd_printk("couldn't read ocm(%d)\n", err); 6338c2ecf20Sopenharmony_ci /* While suspicios, it is not an error that we 6348c2ecf20Sopenharmony_ci * couldn't read the OCM. */ 6358c2ecf20Sopenharmony_ci } 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci err = asd_read_flash(asd_ha); 6388c2ecf20Sopenharmony_ci if (err) { 6398c2ecf20Sopenharmony_ci asd_printk("couldn't read flash(%d)\n", err); 6408c2ecf20Sopenharmony_ci /* While suspicios, it is not an error that we 6418c2ecf20Sopenharmony_ci * couldn't read FLASH memory. 6428c2ecf20Sopenharmony_ci */ 6438c2ecf20Sopenharmony_ci } 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci asd_init_ctxmem(asd_ha); 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci if (asd_get_user_sas_addr(asd_ha)) { 6488c2ecf20Sopenharmony_ci asd_printk("No SAS Address provided for %s\n", 6498c2ecf20Sopenharmony_ci pci_name(asd_ha->pcidev)); 6508c2ecf20Sopenharmony_ci err = -ENODEV; 6518c2ecf20Sopenharmony_ci goto Out; 6528c2ecf20Sopenharmony_ci } 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci asd_propagate_sas_addr(asd_ha); 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci err = asd_init_phys(asd_ha); 6578c2ecf20Sopenharmony_ci if (err) { 6588c2ecf20Sopenharmony_ci asd_printk("couldn't initialize phys for %s\n", 6598c2ecf20Sopenharmony_ci pci_name(asd_ha->pcidev)); 6608c2ecf20Sopenharmony_ci goto Out; 6618c2ecf20Sopenharmony_ci } 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci asd_init_ports(asd_ha); 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci err = asd_init_scbs(asd_ha); 6668c2ecf20Sopenharmony_ci if (err) { 6678c2ecf20Sopenharmony_ci asd_printk("couldn't initialize scbs for %s\n", 6688c2ecf20Sopenharmony_ci pci_name(asd_ha->pcidev)); 6698c2ecf20Sopenharmony_ci goto Out; 6708c2ecf20Sopenharmony_ci } 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci err = asd_init_dl(asd_ha); 6738c2ecf20Sopenharmony_ci if (err) { 6748c2ecf20Sopenharmony_ci asd_printk("couldn't initialize the done list:%d\n", 6758c2ecf20Sopenharmony_ci err); 6768c2ecf20Sopenharmony_ci goto Out; 6778c2ecf20Sopenharmony_ci } 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci err = asd_init_escbs(asd_ha); 6808c2ecf20Sopenharmony_ci if (err) { 6818c2ecf20Sopenharmony_ci asd_printk("couldn't initialize escbs\n"); 6828c2ecf20Sopenharmony_ci goto Out; 6838c2ecf20Sopenharmony_ci } 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci err = asd_init_chip(asd_ha); 6868c2ecf20Sopenharmony_ci if (err) { 6878c2ecf20Sopenharmony_ci asd_printk("couldn't init the chip\n"); 6888c2ecf20Sopenharmony_ci goto Out; 6898c2ecf20Sopenharmony_ci } 6908c2ecf20Sopenharmony_ciOut: 6918c2ecf20Sopenharmony_ci return err; 6928c2ecf20Sopenharmony_ci} 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_ci/* ---------- Chip reset ---------- */ 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci/** 6978c2ecf20Sopenharmony_ci * asd_chip_reset -- reset the host adapter, etc 6988c2ecf20Sopenharmony_ci * @asd_ha: pointer to host adapter structure of interest 6998c2ecf20Sopenharmony_ci * 7008c2ecf20Sopenharmony_ci * Called from the ISR. Hard reset the chip. Let everything 7018c2ecf20Sopenharmony_ci * timeout. This should be no different than hot-unplugging the 7028c2ecf20Sopenharmony_ci * host adapter. Once everything times out we'll init the chip with 7038c2ecf20Sopenharmony_ci * a call to asd_init_chip() and enable interrupts with asd_enable_ints(). 7048c2ecf20Sopenharmony_ci * XXX finish. 7058c2ecf20Sopenharmony_ci */ 7068c2ecf20Sopenharmony_cistatic void asd_chip_reset(struct asd_ha_struct *asd_ha) 7078c2ecf20Sopenharmony_ci{ 7088c2ecf20Sopenharmony_ci ASD_DPRINTK("chip reset for %s\n", pci_name(asd_ha->pcidev)); 7098c2ecf20Sopenharmony_ci asd_chip_hardrst(asd_ha); 7108c2ecf20Sopenharmony_ci} 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci/* ---------- Done List Routines ---------- */ 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_cistatic void asd_dl_tasklet_handler(unsigned long data) 7158c2ecf20Sopenharmony_ci{ 7168c2ecf20Sopenharmony_ci struct asd_ha_struct *asd_ha = (struct asd_ha_struct *) data; 7178c2ecf20Sopenharmony_ci struct asd_seq_data *seq = &asd_ha->seq; 7188c2ecf20Sopenharmony_ci unsigned long flags; 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci while (1) { 7218c2ecf20Sopenharmony_ci struct done_list_struct *dl = &seq->dl[seq->dl_next]; 7228c2ecf20Sopenharmony_ci struct asd_ascb *ascb; 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_ci if ((dl->toggle & DL_TOGGLE_MASK) != seq->dl_toggle) 7258c2ecf20Sopenharmony_ci break; 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci /* find the aSCB */ 7288c2ecf20Sopenharmony_ci spin_lock_irqsave(&seq->tc_index_lock, flags); 7298c2ecf20Sopenharmony_ci ascb = asd_tc_index_find(seq, (int)le16_to_cpu(dl->index)); 7308c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&seq->tc_index_lock, flags); 7318c2ecf20Sopenharmony_ci if (unlikely(!ascb)) { 7328c2ecf20Sopenharmony_ci ASD_DPRINTK("BUG:sequencer:dl:no ascb?!\n"); 7338c2ecf20Sopenharmony_ci goto next_1; 7348c2ecf20Sopenharmony_ci } else if (ascb->scb->header.opcode == EMPTY_SCB) { 7358c2ecf20Sopenharmony_ci goto out; 7368c2ecf20Sopenharmony_ci } else if (!ascb->uldd_timer && !del_timer(&ascb->timer)) { 7378c2ecf20Sopenharmony_ci goto next_1; 7388c2ecf20Sopenharmony_ci } 7398c2ecf20Sopenharmony_ci spin_lock_irqsave(&seq->pend_q_lock, flags); 7408c2ecf20Sopenharmony_ci list_del_init(&ascb->list); 7418c2ecf20Sopenharmony_ci seq->pending--; 7428c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&seq->pend_q_lock, flags); 7438c2ecf20Sopenharmony_ci out: 7448c2ecf20Sopenharmony_ci ascb->tasklet_complete(ascb, dl); 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_ci next_1: 7478c2ecf20Sopenharmony_ci seq->dl_next = (seq->dl_next + 1) & (ASD_DL_SIZE-1); 7488c2ecf20Sopenharmony_ci if (!seq->dl_next) 7498c2ecf20Sopenharmony_ci seq->dl_toggle ^= DL_TOGGLE_MASK; 7508c2ecf20Sopenharmony_ci } 7518c2ecf20Sopenharmony_ci} 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci/* ---------- Interrupt Service Routines ---------- */ 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ci/** 7568c2ecf20Sopenharmony_ci * asd_process_donelist_isr -- schedule processing of done list entries 7578c2ecf20Sopenharmony_ci * @asd_ha: pointer to host adapter structure 7588c2ecf20Sopenharmony_ci */ 7598c2ecf20Sopenharmony_cistatic void asd_process_donelist_isr(struct asd_ha_struct *asd_ha) 7608c2ecf20Sopenharmony_ci{ 7618c2ecf20Sopenharmony_ci tasklet_schedule(&asd_ha->seq.dl_tasklet); 7628c2ecf20Sopenharmony_ci} 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci/** 7658c2ecf20Sopenharmony_ci * asd_com_sas_isr -- process device communication interrupt (COMINT) 7668c2ecf20Sopenharmony_ci * @asd_ha: pointer to host adapter structure 7678c2ecf20Sopenharmony_ci */ 7688c2ecf20Sopenharmony_cistatic void asd_com_sas_isr(struct asd_ha_struct *asd_ha) 7698c2ecf20Sopenharmony_ci{ 7708c2ecf20Sopenharmony_ci u32 comstat = asd_read_reg_dword(asd_ha, COMSTAT); 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_ci /* clear COMSTAT int */ 7738c2ecf20Sopenharmony_ci asd_write_reg_dword(asd_ha, COMSTAT, 0xFFFFFFFF); 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci if (comstat & CSBUFPERR) { 7768c2ecf20Sopenharmony_ci asd_printk("%s: command/status buffer dma parity error\n", 7778c2ecf20Sopenharmony_ci pci_name(asd_ha->pcidev)); 7788c2ecf20Sopenharmony_ci } else if (comstat & CSERR) { 7798c2ecf20Sopenharmony_ci int i; 7808c2ecf20Sopenharmony_ci u32 dmaerr = asd_read_reg_dword(asd_ha, DMAERR); 7818c2ecf20Sopenharmony_ci dmaerr &= 0xFF; 7828c2ecf20Sopenharmony_ci asd_printk("%s: command/status dma error, DMAERR: 0x%02x, " 7838c2ecf20Sopenharmony_ci "CSDMAADR: 0x%04x, CSDMAADR+4: 0x%04x\n", 7848c2ecf20Sopenharmony_ci pci_name(asd_ha->pcidev), 7858c2ecf20Sopenharmony_ci dmaerr, 7868c2ecf20Sopenharmony_ci asd_read_reg_dword(asd_ha, CSDMAADR), 7878c2ecf20Sopenharmony_ci asd_read_reg_dword(asd_ha, CSDMAADR+4)); 7888c2ecf20Sopenharmony_ci asd_printk("CSBUFFER:\n"); 7898c2ecf20Sopenharmony_ci for (i = 0; i < 8; i++) { 7908c2ecf20Sopenharmony_ci asd_printk("%08x %08x %08x %08x\n", 7918c2ecf20Sopenharmony_ci asd_read_reg_dword(asd_ha, CSBUFFER), 7928c2ecf20Sopenharmony_ci asd_read_reg_dword(asd_ha, CSBUFFER+4), 7938c2ecf20Sopenharmony_ci asd_read_reg_dword(asd_ha, CSBUFFER+8), 7948c2ecf20Sopenharmony_ci asd_read_reg_dword(asd_ha, CSBUFFER+12)); 7958c2ecf20Sopenharmony_ci } 7968c2ecf20Sopenharmony_ci asd_dump_seq_state(asd_ha, 0); 7978c2ecf20Sopenharmony_ci } else if (comstat & OVLYERR) { 7988c2ecf20Sopenharmony_ci u32 dmaerr = asd_read_reg_dword(asd_ha, DMAERR); 7998c2ecf20Sopenharmony_ci dmaerr = (dmaerr >> 8) & 0xFF; 8008c2ecf20Sopenharmony_ci asd_printk("%s: overlay dma error:0x%x\n", 8018c2ecf20Sopenharmony_ci pci_name(asd_ha->pcidev), 8028c2ecf20Sopenharmony_ci dmaerr); 8038c2ecf20Sopenharmony_ci } 8048c2ecf20Sopenharmony_ci asd_chip_reset(asd_ha); 8058c2ecf20Sopenharmony_ci} 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_cistatic void asd_arp2_err(struct asd_ha_struct *asd_ha, u32 dchstatus) 8088c2ecf20Sopenharmony_ci{ 8098c2ecf20Sopenharmony_ci static const char *halt_code[256] = { 8108c2ecf20Sopenharmony_ci "UNEXPECTED_INTERRUPT0", 8118c2ecf20Sopenharmony_ci "UNEXPECTED_INTERRUPT1", 8128c2ecf20Sopenharmony_ci "UNEXPECTED_INTERRUPT2", 8138c2ecf20Sopenharmony_ci "UNEXPECTED_INTERRUPT3", 8148c2ecf20Sopenharmony_ci "UNEXPECTED_INTERRUPT4", 8158c2ecf20Sopenharmony_ci "UNEXPECTED_INTERRUPT5", 8168c2ecf20Sopenharmony_ci "UNEXPECTED_INTERRUPT6", 8178c2ecf20Sopenharmony_ci "UNEXPECTED_INTERRUPT7", 8188c2ecf20Sopenharmony_ci "UNEXPECTED_INTERRUPT8", 8198c2ecf20Sopenharmony_ci "UNEXPECTED_INTERRUPT9", 8208c2ecf20Sopenharmony_ci "UNEXPECTED_INTERRUPT10", 8218c2ecf20Sopenharmony_ci [11 ... 19] = "unknown[11,19]", 8228c2ecf20Sopenharmony_ci "NO_FREE_SCB_AVAILABLE", 8238c2ecf20Sopenharmony_ci "INVALID_SCB_OPCODE", 8248c2ecf20Sopenharmony_ci "INVALID_MBX_OPCODE", 8258c2ecf20Sopenharmony_ci "INVALID_ATA_STATE", 8268c2ecf20Sopenharmony_ci "ATA_QUEUE_FULL", 8278c2ecf20Sopenharmony_ci "ATA_TAG_TABLE_FAULT", 8288c2ecf20Sopenharmony_ci "ATA_TAG_MASK_FAULT", 8298c2ecf20Sopenharmony_ci "BAD_LINK_QUEUE_STATE", 8308c2ecf20Sopenharmony_ci "DMA2CHIM_QUEUE_ERROR", 8318c2ecf20Sopenharmony_ci "EMPTY_SCB_LIST_FULL", 8328c2ecf20Sopenharmony_ci "unknown[30]", 8338c2ecf20Sopenharmony_ci "IN_USE_SCB_ON_FREE_LIST", 8348c2ecf20Sopenharmony_ci "BAD_OPEN_WAIT_STATE", 8358c2ecf20Sopenharmony_ci "INVALID_STP_AFFILIATION", 8368c2ecf20Sopenharmony_ci "unknown[34]", 8378c2ecf20Sopenharmony_ci "EXEC_QUEUE_ERROR", 8388c2ecf20Sopenharmony_ci "TOO_MANY_EMPTIES_NEEDED", 8398c2ecf20Sopenharmony_ci "EMPTY_REQ_QUEUE_ERROR", 8408c2ecf20Sopenharmony_ci "Q_MONIRTT_MGMT_ERROR", 8418c2ecf20Sopenharmony_ci "TARGET_MODE_FLOW_ERROR", 8428c2ecf20Sopenharmony_ci "DEVICE_QUEUE_NOT_FOUND", 8438c2ecf20Sopenharmony_ci "START_IRTT_TIMER_ERROR", 8448c2ecf20Sopenharmony_ci "ABORT_TASK_ILLEGAL_REQ", 8458c2ecf20Sopenharmony_ci [43 ... 255] = "unknown[43,255]" 8468c2ecf20Sopenharmony_ci }; 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_ci if (dchstatus & CSEQINT) { 8498c2ecf20Sopenharmony_ci u32 arp2int = asd_read_reg_dword(asd_ha, CARP2INT); 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_ci if (arp2int & (ARP2WAITTO|ARP2ILLOPC|ARP2PERR|ARP2CIOPERR)) { 8528c2ecf20Sopenharmony_ci asd_printk("%s: CSEQ arp2int:0x%x\n", 8538c2ecf20Sopenharmony_ci pci_name(asd_ha->pcidev), 8548c2ecf20Sopenharmony_ci arp2int); 8558c2ecf20Sopenharmony_ci } else if (arp2int & ARP2HALTC) 8568c2ecf20Sopenharmony_ci asd_printk("%s: CSEQ halted: %s\n", 8578c2ecf20Sopenharmony_ci pci_name(asd_ha->pcidev), 8588c2ecf20Sopenharmony_ci halt_code[(arp2int>>16)&0xFF]); 8598c2ecf20Sopenharmony_ci else 8608c2ecf20Sopenharmony_ci asd_printk("%s: CARP2INT:0x%x\n", 8618c2ecf20Sopenharmony_ci pci_name(asd_ha->pcidev), 8628c2ecf20Sopenharmony_ci arp2int); 8638c2ecf20Sopenharmony_ci } 8648c2ecf20Sopenharmony_ci if (dchstatus & LSEQINT_MASK) { 8658c2ecf20Sopenharmony_ci int lseq; 8668c2ecf20Sopenharmony_ci u8 lseq_mask = dchstatus & LSEQINT_MASK; 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ci for_each_sequencer(lseq_mask, lseq_mask, lseq) { 8698c2ecf20Sopenharmony_ci u32 arp2int = asd_read_reg_dword(asd_ha, 8708c2ecf20Sopenharmony_ci LmARP2INT(lseq)); 8718c2ecf20Sopenharmony_ci if (arp2int & (ARP2WAITTO | ARP2ILLOPC | ARP2PERR 8728c2ecf20Sopenharmony_ci | ARP2CIOPERR)) { 8738c2ecf20Sopenharmony_ci asd_printk("%s: LSEQ%d arp2int:0x%x\n", 8748c2ecf20Sopenharmony_ci pci_name(asd_ha->pcidev), 8758c2ecf20Sopenharmony_ci lseq, arp2int); 8768c2ecf20Sopenharmony_ci /* XXX we should only do lseq reset */ 8778c2ecf20Sopenharmony_ci } else if (arp2int & ARP2HALTC) 8788c2ecf20Sopenharmony_ci asd_printk("%s: LSEQ%d halted: %s\n", 8798c2ecf20Sopenharmony_ci pci_name(asd_ha->pcidev), 8808c2ecf20Sopenharmony_ci lseq,halt_code[(arp2int>>16)&0xFF]); 8818c2ecf20Sopenharmony_ci else 8828c2ecf20Sopenharmony_ci asd_printk("%s: LSEQ%d ARP2INT:0x%x\n", 8838c2ecf20Sopenharmony_ci pci_name(asd_ha->pcidev), lseq, 8848c2ecf20Sopenharmony_ci arp2int); 8858c2ecf20Sopenharmony_ci } 8868c2ecf20Sopenharmony_ci } 8878c2ecf20Sopenharmony_ci asd_chip_reset(asd_ha); 8888c2ecf20Sopenharmony_ci} 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_ci/** 8918c2ecf20Sopenharmony_ci * asd_dch_sas_isr -- process device channel interrupt (DEVINT) 8928c2ecf20Sopenharmony_ci * @asd_ha: pointer to host adapter structure 8938c2ecf20Sopenharmony_ci */ 8948c2ecf20Sopenharmony_cistatic void asd_dch_sas_isr(struct asd_ha_struct *asd_ha) 8958c2ecf20Sopenharmony_ci{ 8968c2ecf20Sopenharmony_ci u32 dchstatus = asd_read_reg_dword(asd_ha, DCHSTATUS); 8978c2ecf20Sopenharmony_ci 8988c2ecf20Sopenharmony_ci if (dchstatus & CFIFTOERR) { 8998c2ecf20Sopenharmony_ci asd_printk("%s: CFIFTOERR\n", pci_name(asd_ha->pcidev)); 9008c2ecf20Sopenharmony_ci asd_chip_reset(asd_ha); 9018c2ecf20Sopenharmony_ci } else 9028c2ecf20Sopenharmony_ci asd_arp2_err(asd_ha, dchstatus); 9038c2ecf20Sopenharmony_ci} 9048c2ecf20Sopenharmony_ci 9058c2ecf20Sopenharmony_ci/** 9068c2ecf20Sopenharmony_ci * ads_rbi_exsi_isr -- process external system interface interrupt (INITERR) 9078c2ecf20Sopenharmony_ci * @asd_ha: pointer to host adapter structure 9088c2ecf20Sopenharmony_ci */ 9098c2ecf20Sopenharmony_cistatic void asd_rbi_exsi_isr(struct asd_ha_struct *asd_ha) 9108c2ecf20Sopenharmony_ci{ 9118c2ecf20Sopenharmony_ci u32 stat0r = asd_read_reg_dword(asd_ha, ASISTAT0R); 9128c2ecf20Sopenharmony_ci 9138c2ecf20Sopenharmony_ci if (!(stat0r & ASIERR)) { 9148c2ecf20Sopenharmony_ci asd_printk("hmm, EXSI interrupted but no error?\n"); 9158c2ecf20Sopenharmony_ci return; 9168c2ecf20Sopenharmony_ci } 9178c2ecf20Sopenharmony_ci 9188c2ecf20Sopenharmony_ci if (stat0r & ASIFMTERR) { 9198c2ecf20Sopenharmony_ci asd_printk("ASI SEEPROM format error for %s\n", 9208c2ecf20Sopenharmony_ci pci_name(asd_ha->pcidev)); 9218c2ecf20Sopenharmony_ci } else if (stat0r & ASISEECHKERR) { 9228c2ecf20Sopenharmony_ci u32 stat1r = asd_read_reg_dword(asd_ha, ASISTAT1R); 9238c2ecf20Sopenharmony_ci asd_printk("ASI SEEPROM checksum 0x%x error for %s\n", 9248c2ecf20Sopenharmony_ci stat1r & CHECKSUM_MASK, 9258c2ecf20Sopenharmony_ci pci_name(asd_ha->pcidev)); 9268c2ecf20Sopenharmony_ci } else { 9278c2ecf20Sopenharmony_ci u32 statr = asd_read_reg_dword(asd_ha, ASIERRSTATR); 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_ci if (!(statr & CPI2ASIMSTERR_MASK)) { 9308c2ecf20Sopenharmony_ci ASD_DPRINTK("hmm, ASIERR?\n"); 9318c2ecf20Sopenharmony_ci return; 9328c2ecf20Sopenharmony_ci } else { 9338c2ecf20Sopenharmony_ci u32 addr = asd_read_reg_dword(asd_ha, ASIERRADDR); 9348c2ecf20Sopenharmony_ci u32 data = asd_read_reg_dword(asd_ha, ASIERRDATAR); 9358c2ecf20Sopenharmony_ci 9368c2ecf20Sopenharmony_ci asd_printk("%s: CPI2 xfer err: addr: 0x%x, wdata: 0x%x, " 9378c2ecf20Sopenharmony_ci "count: 0x%x, byteen: 0x%x, targerr: 0x%x " 9388c2ecf20Sopenharmony_ci "master id: 0x%x, master err: 0x%x\n", 9398c2ecf20Sopenharmony_ci pci_name(asd_ha->pcidev), 9408c2ecf20Sopenharmony_ci addr, data, 9418c2ecf20Sopenharmony_ci (statr & CPI2ASIBYTECNT_MASK) >> 16, 9428c2ecf20Sopenharmony_ci (statr & CPI2ASIBYTEEN_MASK) >> 12, 9438c2ecf20Sopenharmony_ci (statr & CPI2ASITARGERR_MASK) >> 8, 9448c2ecf20Sopenharmony_ci (statr & CPI2ASITARGMID_MASK) >> 4, 9458c2ecf20Sopenharmony_ci (statr & CPI2ASIMSTERR_MASK)); 9468c2ecf20Sopenharmony_ci } 9478c2ecf20Sopenharmony_ci } 9488c2ecf20Sopenharmony_ci asd_chip_reset(asd_ha); 9498c2ecf20Sopenharmony_ci} 9508c2ecf20Sopenharmony_ci 9518c2ecf20Sopenharmony_ci/** 9528c2ecf20Sopenharmony_ci * asd_hst_pcix_isr -- process host interface interrupts 9538c2ecf20Sopenharmony_ci * @asd_ha: pointer to host adapter structure 9548c2ecf20Sopenharmony_ci * 9558c2ecf20Sopenharmony_ci * Asserted on PCIX errors: target abort, etc. 9568c2ecf20Sopenharmony_ci */ 9578c2ecf20Sopenharmony_cistatic void asd_hst_pcix_isr(struct asd_ha_struct *asd_ha) 9588c2ecf20Sopenharmony_ci{ 9598c2ecf20Sopenharmony_ci u16 status; 9608c2ecf20Sopenharmony_ci u32 pcix_status; 9618c2ecf20Sopenharmony_ci u32 ecc_status; 9628c2ecf20Sopenharmony_ci 9638c2ecf20Sopenharmony_ci pci_read_config_word(asd_ha->pcidev, PCI_STATUS, &status); 9648c2ecf20Sopenharmony_ci pci_read_config_dword(asd_ha->pcidev, PCIX_STATUS, &pcix_status); 9658c2ecf20Sopenharmony_ci pci_read_config_dword(asd_ha->pcidev, ECC_CTRL_STAT, &ecc_status); 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_ci if (status & PCI_STATUS_DETECTED_PARITY) 9688c2ecf20Sopenharmony_ci asd_printk("parity error for %s\n", pci_name(asd_ha->pcidev)); 9698c2ecf20Sopenharmony_ci else if (status & PCI_STATUS_REC_MASTER_ABORT) 9708c2ecf20Sopenharmony_ci asd_printk("master abort for %s\n", pci_name(asd_ha->pcidev)); 9718c2ecf20Sopenharmony_ci else if (status & PCI_STATUS_REC_TARGET_ABORT) 9728c2ecf20Sopenharmony_ci asd_printk("target abort for %s\n", pci_name(asd_ha->pcidev)); 9738c2ecf20Sopenharmony_ci else if (status & PCI_STATUS_PARITY) 9748c2ecf20Sopenharmony_ci asd_printk("data parity for %s\n", pci_name(asd_ha->pcidev)); 9758c2ecf20Sopenharmony_ci else if (pcix_status & RCV_SCE) { 9768c2ecf20Sopenharmony_ci asd_printk("received split completion error for %s\n", 9778c2ecf20Sopenharmony_ci pci_name(asd_ha->pcidev)); 9788c2ecf20Sopenharmony_ci pci_write_config_dword(asd_ha->pcidev,PCIX_STATUS,pcix_status); 9798c2ecf20Sopenharmony_ci /* XXX: Abort task? */ 9808c2ecf20Sopenharmony_ci return; 9818c2ecf20Sopenharmony_ci } else if (pcix_status & UNEXP_SC) { 9828c2ecf20Sopenharmony_ci asd_printk("unexpected split completion for %s\n", 9838c2ecf20Sopenharmony_ci pci_name(asd_ha->pcidev)); 9848c2ecf20Sopenharmony_ci pci_write_config_dword(asd_ha->pcidev,PCIX_STATUS,pcix_status); 9858c2ecf20Sopenharmony_ci /* ignore */ 9868c2ecf20Sopenharmony_ci return; 9878c2ecf20Sopenharmony_ci } else if (pcix_status & SC_DISCARD) 9888c2ecf20Sopenharmony_ci asd_printk("split completion discarded for %s\n", 9898c2ecf20Sopenharmony_ci pci_name(asd_ha->pcidev)); 9908c2ecf20Sopenharmony_ci else if (ecc_status & UNCOR_ECCERR) 9918c2ecf20Sopenharmony_ci asd_printk("uncorrectable ECC error for %s\n", 9928c2ecf20Sopenharmony_ci pci_name(asd_ha->pcidev)); 9938c2ecf20Sopenharmony_ci asd_chip_reset(asd_ha); 9948c2ecf20Sopenharmony_ci} 9958c2ecf20Sopenharmony_ci 9968c2ecf20Sopenharmony_ci/** 9978c2ecf20Sopenharmony_ci * asd_hw_isr -- host adapter interrupt service routine 9988c2ecf20Sopenharmony_ci * @irq: ignored 9998c2ecf20Sopenharmony_ci * @dev_id: pointer to host adapter structure 10008c2ecf20Sopenharmony_ci * 10018c2ecf20Sopenharmony_ci * The ISR processes done list entries and level 3 error handling. 10028c2ecf20Sopenharmony_ci */ 10038c2ecf20Sopenharmony_ciirqreturn_t asd_hw_isr(int irq, void *dev_id) 10048c2ecf20Sopenharmony_ci{ 10058c2ecf20Sopenharmony_ci struct asd_ha_struct *asd_ha = dev_id; 10068c2ecf20Sopenharmony_ci u32 chimint = asd_read_reg_dword(asd_ha, CHIMINT); 10078c2ecf20Sopenharmony_ci 10088c2ecf20Sopenharmony_ci if (!chimint) 10098c2ecf20Sopenharmony_ci return IRQ_NONE; 10108c2ecf20Sopenharmony_ci 10118c2ecf20Sopenharmony_ci asd_write_reg_dword(asd_ha, CHIMINT, chimint); 10128c2ecf20Sopenharmony_ci (void) asd_read_reg_dword(asd_ha, CHIMINT); 10138c2ecf20Sopenharmony_ci 10148c2ecf20Sopenharmony_ci if (chimint & DLAVAIL) 10158c2ecf20Sopenharmony_ci asd_process_donelist_isr(asd_ha); 10168c2ecf20Sopenharmony_ci if (chimint & COMINT) 10178c2ecf20Sopenharmony_ci asd_com_sas_isr(asd_ha); 10188c2ecf20Sopenharmony_ci if (chimint & DEVINT) 10198c2ecf20Sopenharmony_ci asd_dch_sas_isr(asd_ha); 10208c2ecf20Sopenharmony_ci if (chimint & INITERR) 10218c2ecf20Sopenharmony_ci asd_rbi_exsi_isr(asd_ha); 10228c2ecf20Sopenharmony_ci if (chimint & HOSTERR) 10238c2ecf20Sopenharmony_ci asd_hst_pcix_isr(asd_ha); 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_ci return IRQ_HANDLED; 10268c2ecf20Sopenharmony_ci} 10278c2ecf20Sopenharmony_ci 10288c2ecf20Sopenharmony_ci/* ---------- SCB handling ---------- */ 10298c2ecf20Sopenharmony_ci 10308c2ecf20Sopenharmony_cistatic struct asd_ascb *asd_ascb_alloc(struct asd_ha_struct *asd_ha, 10318c2ecf20Sopenharmony_ci gfp_t gfp_flags) 10328c2ecf20Sopenharmony_ci{ 10338c2ecf20Sopenharmony_ci extern struct kmem_cache *asd_ascb_cache; 10348c2ecf20Sopenharmony_ci struct asd_seq_data *seq = &asd_ha->seq; 10358c2ecf20Sopenharmony_ci struct asd_ascb *ascb; 10368c2ecf20Sopenharmony_ci unsigned long flags; 10378c2ecf20Sopenharmony_ci 10388c2ecf20Sopenharmony_ci ascb = kmem_cache_zalloc(asd_ascb_cache, gfp_flags); 10398c2ecf20Sopenharmony_ci 10408c2ecf20Sopenharmony_ci if (ascb) { 10418c2ecf20Sopenharmony_ci ascb->dma_scb.size = sizeof(struct scb); 10428c2ecf20Sopenharmony_ci ascb->dma_scb.vaddr = dma_pool_zalloc(asd_ha->scb_pool, 10438c2ecf20Sopenharmony_ci gfp_flags, 10448c2ecf20Sopenharmony_ci &ascb->dma_scb.dma_handle); 10458c2ecf20Sopenharmony_ci if (!ascb->dma_scb.vaddr) { 10468c2ecf20Sopenharmony_ci kmem_cache_free(asd_ascb_cache, ascb); 10478c2ecf20Sopenharmony_ci return NULL; 10488c2ecf20Sopenharmony_ci } 10498c2ecf20Sopenharmony_ci asd_init_ascb(asd_ha, ascb); 10508c2ecf20Sopenharmony_ci 10518c2ecf20Sopenharmony_ci spin_lock_irqsave(&seq->tc_index_lock, flags); 10528c2ecf20Sopenharmony_ci ascb->tc_index = asd_tc_index_get(seq, ascb); 10538c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&seq->tc_index_lock, flags); 10548c2ecf20Sopenharmony_ci if (ascb->tc_index == -1) 10558c2ecf20Sopenharmony_ci goto undo; 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_ci ascb->scb->header.index = cpu_to_le16((u16)ascb->tc_index); 10588c2ecf20Sopenharmony_ci } 10598c2ecf20Sopenharmony_ci 10608c2ecf20Sopenharmony_ci return ascb; 10618c2ecf20Sopenharmony_ciundo: 10628c2ecf20Sopenharmony_ci dma_pool_free(asd_ha->scb_pool, ascb->dma_scb.vaddr, 10638c2ecf20Sopenharmony_ci ascb->dma_scb.dma_handle); 10648c2ecf20Sopenharmony_ci kmem_cache_free(asd_ascb_cache, ascb); 10658c2ecf20Sopenharmony_ci ASD_DPRINTK("no index for ascb\n"); 10668c2ecf20Sopenharmony_ci return NULL; 10678c2ecf20Sopenharmony_ci} 10688c2ecf20Sopenharmony_ci 10698c2ecf20Sopenharmony_ci/** 10708c2ecf20Sopenharmony_ci * asd_ascb_alloc_list -- allocate a list of aSCBs 10718c2ecf20Sopenharmony_ci * @asd_ha: pointer to host adapter structure 10728c2ecf20Sopenharmony_ci * @num: pointer to integer number of aSCBs 10738c2ecf20Sopenharmony_ci * @gfp_flags: GFP_ flags. 10748c2ecf20Sopenharmony_ci * 10758c2ecf20Sopenharmony_ci * This is the only function which is used to allocate aSCBs. 10768c2ecf20Sopenharmony_ci * It can allocate one or many. If more than one, then they form 10778c2ecf20Sopenharmony_ci * a linked list in two ways: by their list field of the ascb struct 10788c2ecf20Sopenharmony_ci * and by the next_scb field of the scb_header. 10798c2ecf20Sopenharmony_ci * 10808c2ecf20Sopenharmony_ci * Returns NULL if no memory was available, else pointer to a list 10818c2ecf20Sopenharmony_ci * of ascbs. When this function returns, @num would be the number 10828c2ecf20Sopenharmony_ci * of SCBs which were not able to be allocated, 0 if all requested 10838c2ecf20Sopenharmony_ci * were able to be allocated. 10848c2ecf20Sopenharmony_ci */ 10858c2ecf20Sopenharmony_cistruct asd_ascb *asd_ascb_alloc_list(struct asd_ha_struct 10868c2ecf20Sopenharmony_ci *asd_ha, int *num, 10878c2ecf20Sopenharmony_ci gfp_t gfp_flags) 10888c2ecf20Sopenharmony_ci{ 10898c2ecf20Sopenharmony_ci struct asd_ascb *first = NULL; 10908c2ecf20Sopenharmony_ci 10918c2ecf20Sopenharmony_ci for ( ; *num > 0; --*num) { 10928c2ecf20Sopenharmony_ci struct asd_ascb *ascb = asd_ascb_alloc(asd_ha, gfp_flags); 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_ci if (!ascb) 10958c2ecf20Sopenharmony_ci break; 10968c2ecf20Sopenharmony_ci else if (!first) 10978c2ecf20Sopenharmony_ci first = ascb; 10988c2ecf20Sopenharmony_ci else { 10998c2ecf20Sopenharmony_ci struct asd_ascb *last = list_entry(first->list.prev, 11008c2ecf20Sopenharmony_ci struct asd_ascb, 11018c2ecf20Sopenharmony_ci list); 11028c2ecf20Sopenharmony_ci list_add_tail(&ascb->list, &first->list); 11038c2ecf20Sopenharmony_ci last->scb->header.next_scb = 11048c2ecf20Sopenharmony_ci cpu_to_le64(((u64)ascb->dma_scb.dma_handle)); 11058c2ecf20Sopenharmony_ci } 11068c2ecf20Sopenharmony_ci } 11078c2ecf20Sopenharmony_ci 11088c2ecf20Sopenharmony_ci return first; 11098c2ecf20Sopenharmony_ci} 11108c2ecf20Sopenharmony_ci 11118c2ecf20Sopenharmony_ci/** 11128c2ecf20Sopenharmony_ci * asd_swap_head_scb -- swap the head scb 11138c2ecf20Sopenharmony_ci * @asd_ha: pointer to host adapter structure 11148c2ecf20Sopenharmony_ci * @ascb: pointer to the head of an ascb list 11158c2ecf20Sopenharmony_ci * 11168c2ecf20Sopenharmony_ci * The sequencer knows the DMA address of the next SCB to be DMAed to 11178c2ecf20Sopenharmony_ci * the host adapter, from initialization or from the last list DMAed. 11188c2ecf20Sopenharmony_ci * seq->next_scb keeps the address of this SCB. The sequencer will 11198c2ecf20Sopenharmony_ci * DMA to the host adapter this list of SCBs. But the head (first 11208c2ecf20Sopenharmony_ci * element) of this list is not known to the sequencer. Here we swap 11218c2ecf20Sopenharmony_ci * the head of the list with the known SCB (memcpy()). 11228c2ecf20Sopenharmony_ci * Only one memcpy() is required per list so it is in our interest 11238c2ecf20Sopenharmony_ci * to keep the list of SCB as long as possible so that the ratio 11248c2ecf20Sopenharmony_ci * of number of memcpy calls to the number of SCB DMA-ed is as small 11258c2ecf20Sopenharmony_ci * as possible. 11268c2ecf20Sopenharmony_ci * 11278c2ecf20Sopenharmony_ci * LOCKING: called with the pending list lock held. 11288c2ecf20Sopenharmony_ci */ 11298c2ecf20Sopenharmony_cistatic void asd_swap_head_scb(struct asd_ha_struct *asd_ha, 11308c2ecf20Sopenharmony_ci struct asd_ascb *ascb) 11318c2ecf20Sopenharmony_ci{ 11328c2ecf20Sopenharmony_ci struct asd_seq_data *seq = &asd_ha->seq; 11338c2ecf20Sopenharmony_ci struct asd_ascb *last = list_entry(ascb->list.prev, 11348c2ecf20Sopenharmony_ci struct asd_ascb, 11358c2ecf20Sopenharmony_ci list); 11368c2ecf20Sopenharmony_ci struct asd_dma_tok t = ascb->dma_scb; 11378c2ecf20Sopenharmony_ci 11388c2ecf20Sopenharmony_ci memcpy(seq->next_scb.vaddr, ascb->scb, sizeof(*ascb->scb)); 11398c2ecf20Sopenharmony_ci ascb->dma_scb = seq->next_scb; 11408c2ecf20Sopenharmony_ci ascb->scb = ascb->dma_scb.vaddr; 11418c2ecf20Sopenharmony_ci seq->next_scb = t; 11428c2ecf20Sopenharmony_ci last->scb->header.next_scb = 11438c2ecf20Sopenharmony_ci cpu_to_le64(((u64)seq->next_scb.dma_handle)); 11448c2ecf20Sopenharmony_ci} 11458c2ecf20Sopenharmony_ci 11468c2ecf20Sopenharmony_ci/** 11478c2ecf20Sopenharmony_ci * asd_start_timers -- (add and) start timers of SCBs 11488c2ecf20Sopenharmony_ci * @list: pointer to struct list_head of the scbs 11498c2ecf20Sopenharmony_ci * 11508c2ecf20Sopenharmony_ci * If an SCB in the @list has no timer function, assign the default 11518c2ecf20Sopenharmony_ci * one, then start the timer of the SCB. This function is 11528c2ecf20Sopenharmony_ci * intended to be called from asd_post_ascb_list(), just prior to 11538c2ecf20Sopenharmony_ci * posting the SCBs to the sequencer. 11548c2ecf20Sopenharmony_ci */ 11558c2ecf20Sopenharmony_cistatic void asd_start_scb_timers(struct list_head *list) 11568c2ecf20Sopenharmony_ci{ 11578c2ecf20Sopenharmony_ci struct asd_ascb *ascb; 11588c2ecf20Sopenharmony_ci list_for_each_entry(ascb, list, list) { 11598c2ecf20Sopenharmony_ci if (!ascb->uldd_timer) { 11608c2ecf20Sopenharmony_ci ascb->timer.function = asd_ascb_timedout; 11618c2ecf20Sopenharmony_ci ascb->timer.expires = jiffies + AIC94XX_SCB_TIMEOUT; 11628c2ecf20Sopenharmony_ci add_timer(&ascb->timer); 11638c2ecf20Sopenharmony_ci } 11648c2ecf20Sopenharmony_ci } 11658c2ecf20Sopenharmony_ci} 11668c2ecf20Sopenharmony_ci 11678c2ecf20Sopenharmony_ci/** 11688c2ecf20Sopenharmony_ci * asd_post_ascb_list -- post a list of 1 or more aSCBs to the host adapter 11698c2ecf20Sopenharmony_ci * @asd_ha: pointer to a host adapter structure 11708c2ecf20Sopenharmony_ci * @ascb: pointer to the first aSCB in the list 11718c2ecf20Sopenharmony_ci * @num: number of aSCBs in the list (to be posted) 11728c2ecf20Sopenharmony_ci * 11738c2ecf20Sopenharmony_ci * See queueing comment in asd_post_escb_list(). 11748c2ecf20Sopenharmony_ci * 11758c2ecf20Sopenharmony_ci * Additional note on queuing: In order to minimize the ratio of memcpy() 11768c2ecf20Sopenharmony_ci * to the number of ascbs sent, we try to batch-send as many ascbs as possible 11778c2ecf20Sopenharmony_ci * in one go. 11788c2ecf20Sopenharmony_ci * Two cases are possible: 11798c2ecf20Sopenharmony_ci * A) can_queue >= num, 11808c2ecf20Sopenharmony_ci * B) can_queue < num. 11818c2ecf20Sopenharmony_ci * Case A: we can send the whole batch at once. Increment "pending" 11828c2ecf20Sopenharmony_ci * in the beginning of this function, when it is checked, in order to 11838c2ecf20Sopenharmony_ci * eliminate races when this function is called by multiple processes. 11848c2ecf20Sopenharmony_ci * Case B: should never happen. 11858c2ecf20Sopenharmony_ci */ 11868c2ecf20Sopenharmony_ciint asd_post_ascb_list(struct asd_ha_struct *asd_ha, struct asd_ascb *ascb, 11878c2ecf20Sopenharmony_ci int num) 11888c2ecf20Sopenharmony_ci{ 11898c2ecf20Sopenharmony_ci unsigned long flags; 11908c2ecf20Sopenharmony_ci LIST_HEAD(list); 11918c2ecf20Sopenharmony_ci int can_queue; 11928c2ecf20Sopenharmony_ci 11938c2ecf20Sopenharmony_ci spin_lock_irqsave(&asd_ha->seq.pend_q_lock, flags); 11948c2ecf20Sopenharmony_ci can_queue = asd_ha->hw_prof.max_scbs - asd_ha->seq.pending; 11958c2ecf20Sopenharmony_ci if (can_queue >= num) 11968c2ecf20Sopenharmony_ci asd_ha->seq.pending += num; 11978c2ecf20Sopenharmony_ci else 11988c2ecf20Sopenharmony_ci can_queue = 0; 11998c2ecf20Sopenharmony_ci 12008c2ecf20Sopenharmony_ci if (!can_queue) { 12018c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&asd_ha->seq.pend_q_lock, flags); 12028c2ecf20Sopenharmony_ci asd_printk("%s: scb queue full\n", pci_name(asd_ha->pcidev)); 12038c2ecf20Sopenharmony_ci return -SAS_QUEUE_FULL; 12048c2ecf20Sopenharmony_ci } 12058c2ecf20Sopenharmony_ci 12068c2ecf20Sopenharmony_ci asd_swap_head_scb(asd_ha, ascb); 12078c2ecf20Sopenharmony_ci 12088c2ecf20Sopenharmony_ci __list_add(&list, ascb->list.prev, &ascb->list); 12098c2ecf20Sopenharmony_ci 12108c2ecf20Sopenharmony_ci asd_start_scb_timers(&list); 12118c2ecf20Sopenharmony_ci 12128c2ecf20Sopenharmony_ci asd_ha->seq.scbpro += num; 12138c2ecf20Sopenharmony_ci list_splice_init(&list, asd_ha->seq.pend_q.prev); 12148c2ecf20Sopenharmony_ci asd_write_reg_dword(asd_ha, SCBPRO, (u32)asd_ha->seq.scbpro); 12158c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&asd_ha->seq.pend_q_lock, flags); 12168c2ecf20Sopenharmony_ci 12178c2ecf20Sopenharmony_ci return 0; 12188c2ecf20Sopenharmony_ci} 12198c2ecf20Sopenharmony_ci 12208c2ecf20Sopenharmony_ci/** 12218c2ecf20Sopenharmony_ci * asd_post_escb_list -- post a list of 1 or more empty scb 12228c2ecf20Sopenharmony_ci * @asd_ha: pointer to a host adapter structure 12238c2ecf20Sopenharmony_ci * @ascb: pointer to the first empty SCB in the list 12248c2ecf20Sopenharmony_ci * @num: number of aSCBs in the list (to be posted) 12258c2ecf20Sopenharmony_ci * 12268c2ecf20Sopenharmony_ci * This is essentially the same as asd_post_ascb_list, but we do not 12278c2ecf20Sopenharmony_ci * increment pending, add those to the pending list or get indexes. 12288c2ecf20Sopenharmony_ci * See asd_init_escbs() and asd_init_post_escbs(). 12298c2ecf20Sopenharmony_ci * 12308c2ecf20Sopenharmony_ci * Since sending a list of ascbs is a superset of sending a single 12318c2ecf20Sopenharmony_ci * ascb, this function exists to generalize this. More specifically, 12328c2ecf20Sopenharmony_ci * when sending a list of those, we want to do only a _single_ 12338c2ecf20Sopenharmony_ci * memcpy() at swap head, as opposed to for each ascb sent (in the 12348c2ecf20Sopenharmony_ci * case of sending them one by one). That is, we want to minimize the 12358c2ecf20Sopenharmony_ci * ratio of memcpy() operations to the number of ascbs sent. The same 12368c2ecf20Sopenharmony_ci * logic applies to asd_post_ascb_list(). 12378c2ecf20Sopenharmony_ci */ 12388c2ecf20Sopenharmony_ciint asd_post_escb_list(struct asd_ha_struct *asd_ha, struct asd_ascb *ascb, 12398c2ecf20Sopenharmony_ci int num) 12408c2ecf20Sopenharmony_ci{ 12418c2ecf20Sopenharmony_ci unsigned long flags; 12428c2ecf20Sopenharmony_ci 12438c2ecf20Sopenharmony_ci spin_lock_irqsave(&asd_ha->seq.pend_q_lock, flags); 12448c2ecf20Sopenharmony_ci asd_swap_head_scb(asd_ha, ascb); 12458c2ecf20Sopenharmony_ci asd_ha->seq.scbpro += num; 12468c2ecf20Sopenharmony_ci asd_write_reg_dword(asd_ha, SCBPRO, (u32)asd_ha->seq.scbpro); 12478c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&asd_ha->seq.pend_q_lock, flags); 12488c2ecf20Sopenharmony_ci 12498c2ecf20Sopenharmony_ci return 0; 12508c2ecf20Sopenharmony_ci} 12518c2ecf20Sopenharmony_ci 12528c2ecf20Sopenharmony_ci/* ---------- LED ---------- */ 12538c2ecf20Sopenharmony_ci 12548c2ecf20Sopenharmony_ci/** 12558c2ecf20Sopenharmony_ci * asd_turn_led -- turn on/off an LED 12568c2ecf20Sopenharmony_ci * @asd_ha: pointer to host adapter structure 12578c2ecf20Sopenharmony_ci * @phy_id: the PHY id whose LED we want to manupulate 12588c2ecf20Sopenharmony_ci * @op: 1 to turn on, 0 to turn off 12598c2ecf20Sopenharmony_ci */ 12608c2ecf20Sopenharmony_civoid asd_turn_led(struct asd_ha_struct *asd_ha, int phy_id, int op) 12618c2ecf20Sopenharmony_ci{ 12628c2ecf20Sopenharmony_ci if (phy_id < ASD_MAX_PHYS) { 12638c2ecf20Sopenharmony_ci u32 v = asd_read_reg_dword(asd_ha, LmCONTROL(phy_id)); 12648c2ecf20Sopenharmony_ci if (op) 12658c2ecf20Sopenharmony_ci v |= LEDPOL; 12668c2ecf20Sopenharmony_ci else 12678c2ecf20Sopenharmony_ci v &= ~LEDPOL; 12688c2ecf20Sopenharmony_ci asd_write_reg_dword(asd_ha, LmCONTROL(phy_id), v); 12698c2ecf20Sopenharmony_ci } 12708c2ecf20Sopenharmony_ci} 12718c2ecf20Sopenharmony_ci 12728c2ecf20Sopenharmony_ci/** 12738c2ecf20Sopenharmony_ci * asd_control_led -- enable/disable an LED on the board 12748c2ecf20Sopenharmony_ci * @asd_ha: pointer to host adapter structure 12758c2ecf20Sopenharmony_ci * @phy_id: integer, the phy id 12768c2ecf20Sopenharmony_ci * @op: integer, 1 to enable, 0 to disable the LED 12778c2ecf20Sopenharmony_ci * 12788c2ecf20Sopenharmony_ci * First we output enable the LED, then we set the source 12798c2ecf20Sopenharmony_ci * to be an external module. 12808c2ecf20Sopenharmony_ci */ 12818c2ecf20Sopenharmony_civoid asd_control_led(struct asd_ha_struct *asd_ha, int phy_id, int op) 12828c2ecf20Sopenharmony_ci{ 12838c2ecf20Sopenharmony_ci if (phy_id < ASD_MAX_PHYS) { 12848c2ecf20Sopenharmony_ci u32 v; 12858c2ecf20Sopenharmony_ci 12868c2ecf20Sopenharmony_ci v = asd_read_reg_dword(asd_ha, GPIOOER); 12878c2ecf20Sopenharmony_ci if (op) 12888c2ecf20Sopenharmony_ci v |= (1 << phy_id); 12898c2ecf20Sopenharmony_ci else 12908c2ecf20Sopenharmony_ci v &= ~(1 << phy_id); 12918c2ecf20Sopenharmony_ci asd_write_reg_dword(asd_ha, GPIOOER, v); 12928c2ecf20Sopenharmony_ci 12938c2ecf20Sopenharmony_ci v = asd_read_reg_dword(asd_ha, GPIOCNFGR); 12948c2ecf20Sopenharmony_ci if (op) 12958c2ecf20Sopenharmony_ci v |= (1 << phy_id); 12968c2ecf20Sopenharmony_ci else 12978c2ecf20Sopenharmony_ci v &= ~(1 << phy_id); 12988c2ecf20Sopenharmony_ci asd_write_reg_dword(asd_ha, GPIOCNFGR, v); 12998c2ecf20Sopenharmony_ci } 13008c2ecf20Sopenharmony_ci} 13018c2ecf20Sopenharmony_ci 13028c2ecf20Sopenharmony_ci/* ---------- PHY enable ---------- */ 13038c2ecf20Sopenharmony_ci 13048c2ecf20Sopenharmony_cistatic int asd_enable_phy(struct asd_ha_struct *asd_ha, int phy_id) 13058c2ecf20Sopenharmony_ci{ 13068c2ecf20Sopenharmony_ci struct asd_phy *phy = &asd_ha->phys[phy_id]; 13078c2ecf20Sopenharmony_ci 13088c2ecf20Sopenharmony_ci asd_write_reg_byte(asd_ha, LmSEQ_OOB_REG(phy_id, INT_ENABLE_2), 0); 13098c2ecf20Sopenharmony_ci asd_write_reg_byte(asd_ha, LmSEQ_OOB_REG(phy_id, HOT_PLUG_DELAY), 13108c2ecf20Sopenharmony_ci HOTPLUG_DELAY_TIMEOUT); 13118c2ecf20Sopenharmony_ci 13128c2ecf20Sopenharmony_ci /* Get defaults from manuf. sector */ 13138c2ecf20Sopenharmony_ci /* XXX we need defaults for those in case MS is broken. */ 13148c2ecf20Sopenharmony_ci asd_write_reg_byte(asd_ha, LmSEQ_OOB_REG(phy_id, PHY_CONTROL_0), 13158c2ecf20Sopenharmony_ci phy->phy_desc->phy_control_0); 13168c2ecf20Sopenharmony_ci asd_write_reg_byte(asd_ha, LmSEQ_OOB_REG(phy_id, PHY_CONTROL_1), 13178c2ecf20Sopenharmony_ci phy->phy_desc->phy_control_1); 13188c2ecf20Sopenharmony_ci asd_write_reg_byte(asd_ha, LmSEQ_OOB_REG(phy_id, PHY_CONTROL_2), 13198c2ecf20Sopenharmony_ci phy->phy_desc->phy_control_2); 13208c2ecf20Sopenharmony_ci asd_write_reg_byte(asd_ha, LmSEQ_OOB_REG(phy_id, PHY_CONTROL_3), 13218c2ecf20Sopenharmony_ci phy->phy_desc->phy_control_3); 13228c2ecf20Sopenharmony_ci 13238c2ecf20Sopenharmony_ci asd_write_reg_dword(asd_ha, LmSEQ_TEN_MS_COMINIT_TIMEOUT(phy_id), 13248c2ecf20Sopenharmony_ci ASD_COMINIT_TIMEOUT); 13258c2ecf20Sopenharmony_ci 13268c2ecf20Sopenharmony_ci asd_write_reg_addr(asd_ha, LmSEQ_TX_ID_ADDR_FRAME(phy_id), 13278c2ecf20Sopenharmony_ci phy->id_frm_tok->dma_handle); 13288c2ecf20Sopenharmony_ci 13298c2ecf20Sopenharmony_ci asd_control_led(asd_ha, phy_id, 1); 13308c2ecf20Sopenharmony_ci 13318c2ecf20Sopenharmony_ci return 0; 13328c2ecf20Sopenharmony_ci} 13338c2ecf20Sopenharmony_ci 13348c2ecf20Sopenharmony_ciint asd_enable_phys(struct asd_ha_struct *asd_ha, const u8 phy_mask) 13358c2ecf20Sopenharmony_ci{ 13368c2ecf20Sopenharmony_ci u8 phy_m; 13378c2ecf20Sopenharmony_ci u8 i; 13388c2ecf20Sopenharmony_ci int num = 0, k; 13398c2ecf20Sopenharmony_ci struct asd_ascb *ascb; 13408c2ecf20Sopenharmony_ci struct asd_ascb *ascb_list; 13418c2ecf20Sopenharmony_ci 13428c2ecf20Sopenharmony_ci if (!phy_mask) { 13438c2ecf20Sopenharmony_ci asd_printk("%s called with phy_mask of 0!?\n", __func__); 13448c2ecf20Sopenharmony_ci return 0; 13458c2ecf20Sopenharmony_ci } 13468c2ecf20Sopenharmony_ci 13478c2ecf20Sopenharmony_ci for_each_phy(phy_mask, phy_m, i) { 13488c2ecf20Sopenharmony_ci num++; 13498c2ecf20Sopenharmony_ci asd_enable_phy(asd_ha, i); 13508c2ecf20Sopenharmony_ci } 13518c2ecf20Sopenharmony_ci 13528c2ecf20Sopenharmony_ci k = num; 13538c2ecf20Sopenharmony_ci ascb_list = asd_ascb_alloc_list(asd_ha, &k, GFP_KERNEL); 13548c2ecf20Sopenharmony_ci if (!ascb_list) { 13558c2ecf20Sopenharmony_ci asd_printk("no memory for control phy ascb list\n"); 13568c2ecf20Sopenharmony_ci return -ENOMEM; 13578c2ecf20Sopenharmony_ci } 13588c2ecf20Sopenharmony_ci num -= k; 13598c2ecf20Sopenharmony_ci 13608c2ecf20Sopenharmony_ci ascb = ascb_list; 13618c2ecf20Sopenharmony_ci for_each_phy(phy_mask, phy_m, i) { 13628c2ecf20Sopenharmony_ci asd_build_control_phy(ascb, i, ENABLE_PHY); 13638c2ecf20Sopenharmony_ci ascb = list_entry(ascb->list.next, struct asd_ascb, list); 13648c2ecf20Sopenharmony_ci } 13658c2ecf20Sopenharmony_ci ASD_DPRINTK("posting %d control phy scbs\n", num); 13668c2ecf20Sopenharmony_ci k = asd_post_ascb_list(asd_ha, ascb_list, num); 13678c2ecf20Sopenharmony_ci if (k) 13688c2ecf20Sopenharmony_ci asd_ascb_free_list(ascb_list); 13698c2ecf20Sopenharmony_ci 13708c2ecf20Sopenharmony_ci return k; 13718c2ecf20Sopenharmony_ci} 1372