165c85c83SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
22908d778SJames Bottomley /*
32908d778SJames Bottomley * Aic94xx SAS/SATA driver hardware interface.
42908d778SJames Bottomley *
52908d778SJames Bottomley * Copyright (C) 2005 Adaptec, Inc. All rights reserved.
62908d778SJames Bottomley * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
72908d778SJames Bottomley */
82908d778SJames Bottomley
92908d778SJames Bottomley #include <linux/pci.h>
105a0e3ad6STejun Heo #include <linux/slab.h>
112908d778SJames Bottomley #include <linux/delay.h>
122908d778SJames Bottomley #include <linux/module.h>
1368066c3eSDarrick J. Wong #include <linux/firmware.h>
142908d778SJames Bottomley
152908d778SJames Bottomley #include "aic94xx.h"
162908d778SJames Bottomley #include "aic94xx_reg.h"
172908d778SJames Bottomley #include "aic94xx_hwi.h"
182908d778SJames Bottomley #include "aic94xx_seq.h"
192908d778SJames Bottomley #include "aic94xx_dump.h"
202908d778SJames Bottomley
212908d778SJames Bottomley u32 MBAR0_SWB_SIZE;
222908d778SJames Bottomley
232908d778SJames Bottomley /* ---------- Initialization ---------- */
242908d778SJames Bottomley
asd_get_user_sas_addr(struct asd_ha_struct * asd_ha)2568066c3eSDarrick J. Wong static int asd_get_user_sas_addr(struct asd_ha_struct *asd_ha)
262908d778SJames Bottomley {
2768066c3eSDarrick J. Wong /* adapter came with a sas address */
2868066c3eSDarrick J. Wong if (asd_ha->hw_prof.sas_addr[0])
2968066c3eSDarrick J. Wong return 0;
3068066c3eSDarrick J. Wong
31*1136a022SJohn Garry return sas_request_addr(asd_ha->sas_ha.shost,
3268066c3eSDarrick J. Wong asd_ha->hw_prof.sas_addr);
332908d778SJames Bottomley }
342908d778SJames Bottomley
asd_propagate_sas_addr(struct asd_ha_struct * asd_ha)352908d778SJames Bottomley static void asd_propagate_sas_addr(struct asd_ha_struct *asd_ha)
362908d778SJames Bottomley {
372908d778SJames Bottomley int i;
382908d778SJames Bottomley
392908d778SJames Bottomley for (i = 0; i < ASD_MAX_PHYS; i++) {
402908d778SJames Bottomley if (asd_ha->hw_prof.phy_desc[i].sas_addr[0] == 0)
412908d778SJames Bottomley continue;
422908d778SJames Bottomley /* Set a phy's address only if it has none.
432908d778SJames Bottomley */
442908d778SJames Bottomley ASD_DPRINTK("setting phy%d addr to %llx\n", i,
452908d778SJames Bottomley SAS_ADDR(asd_ha->hw_prof.sas_addr));
462908d778SJames Bottomley memcpy(asd_ha->hw_prof.phy_desc[i].sas_addr,
472908d778SJames Bottomley asd_ha->hw_prof.sas_addr, SAS_ADDR_SIZE);
482908d778SJames Bottomley }
492908d778SJames Bottomley }
502908d778SJames Bottomley
512908d778SJames Bottomley /* ---------- PHY initialization ---------- */
522908d778SJames Bottomley
asd_init_phy_identify(struct asd_phy * phy)532908d778SJames Bottomley static void asd_init_phy_identify(struct asd_phy *phy)
542908d778SJames Bottomley {
552908d778SJames Bottomley phy->identify_frame = phy->id_frm_tok->vaddr;
562908d778SJames Bottomley
572908d778SJames Bottomley memset(phy->identify_frame, 0, sizeof(*phy->identify_frame));
582908d778SJames Bottomley
59aa9f8328SJames Bottomley phy->identify_frame->dev_type = SAS_END_DEVICE;
602908d778SJames Bottomley if (phy->sas_phy.role & PHY_ROLE_INITIATOR)
612908d778SJames Bottomley phy->identify_frame->initiator_bits = phy->sas_phy.iproto;
622908d778SJames Bottomley if (phy->sas_phy.role & PHY_ROLE_TARGET)
632908d778SJames Bottomley phy->identify_frame->target_bits = phy->sas_phy.tproto;
642908d778SJames Bottomley memcpy(phy->identify_frame->sas_addr, phy->phy_desc->sas_addr,
652908d778SJames Bottomley SAS_ADDR_SIZE);
662908d778SJames Bottomley phy->identify_frame->phy_id = phy->sas_phy.id;
672908d778SJames Bottomley }
682908d778SJames Bottomley
asd_init_phy(struct asd_phy * phy)692908d778SJames Bottomley static int asd_init_phy(struct asd_phy *phy)
702908d778SJames Bottomley {
712908d778SJames Bottomley struct asd_ha_struct *asd_ha = phy->sas_phy.ha->lldd_ha;
722908d778SJames Bottomley struct asd_sas_phy *sas_phy = &phy->sas_phy;
732908d778SJames Bottomley
742908d778SJames Bottomley sas_phy->enabled = 1;
755929faf3SDarrick J. Wong sas_phy->iproto = SAS_PROTOCOL_ALL;
762908d778SJames Bottomley sas_phy->tproto = 0;
772908d778SJames Bottomley sas_phy->role = PHY_ROLE_INITIATOR;
782908d778SJames Bottomley sas_phy->oob_mode = OOB_NOT_CONNECTED;
7988edf746SJames Bottomley sas_phy->linkrate = SAS_LINK_RATE_UNKNOWN;
802908d778SJames Bottomley
812908d778SJames Bottomley phy->id_frm_tok = asd_alloc_coherent(asd_ha,
822908d778SJames Bottomley sizeof(*phy->identify_frame),
832908d778SJames Bottomley GFP_KERNEL);
842908d778SJames Bottomley if (!phy->id_frm_tok) {
852908d778SJames Bottomley asd_printk("no mem for IDENTIFY for phy%d\n", sas_phy->id);
862908d778SJames Bottomley return -ENOMEM;
872908d778SJames Bottomley } else
882908d778SJames Bottomley asd_init_phy_identify(phy);
892908d778SJames Bottomley
902908d778SJames Bottomley memset(phy->frame_rcvd, 0, sizeof(phy->frame_rcvd));
912908d778SJames Bottomley
922908d778SJames Bottomley return 0;
932908d778SJames Bottomley }
942908d778SJames Bottomley
asd_init_ports(struct asd_ha_struct * asd_ha)953f048109Smalahal@us.ibm.com static void asd_init_ports(struct asd_ha_struct *asd_ha)
963f048109Smalahal@us.ibm.com {
973f048109Smalahal@us.ibm.com int i;
983f048109Smalahal@us.ibm.com
993f048109Smalahal@us.ibm.com spin_lock_init(&asd_ha->asd_ports_lock);
1003f048109Smalahal@us.ibm.com for (i = 0; i < ASD_MAX_PHYS; i++) {
1013f048109Smalahal@us.ibm.com struct asd_port *asd_port = &asd_ha->asd_ports[i];
1023f048109Smalahal@us.ibm.com
1033f048109Smalahal@us.ibm.com memset(asd_port->sas_addr, 0, SAS_ADDR_SIZE);
1043f048109Smalahal@us.ibm.com memset(asd_port->attached_sas_addr, 0, SAS_ADDR_SIZE);
1053f048109Smalahal@us.ibm.com asd_port->phy_mask = 0;
1063f048109Smalahal@us.ibm.com asd_port->num_phys = 0;
1073f048109Smalahal@us.ibm.com }
1083f048109Smalahal@us.ibm.com }
1093f048109Smalahal@us.ibm.com
asd_init_phys(struct asd_ha_struct * asd_ha)1102908d778SJames Bottomley static int asd_init_phys(struct asd_ha_struct *asd_ha)
1112908d778SJames Bottomley {
1122908d778SJames Bottomley u8 i;
1132908d778SJames Bottomley u8 phy_mask = asd_ha->hw_prof.enabled_phys;
1142908d778SJames Bottomley
1152908d778SJames Bottomley for (i = 0; i < ASD_MAX_PHYS; i++) {
1162908d778SJames Bottomley struct asd_phy *phy = &asd_ha->phys[i];
1172908d778SJames Bottomley
1182908d778SJames Bottomley phy->phy_desc = &asd_ha->hw_prof.phy_desc[i];
1193f048109Smalahal@us.ibm.com phy->asd_port = NULL;
1202908d778SJames Bottomley
1212908d778SJames Bottomley phy->sas_phy.enabled = 0;
1222908d778SJames Bottomley phy->sas_phy.id = i;
1232908d778SJames Bottomley phy->sas_phy.sas_addr = &phy->phy_desc->sas_addr[0];
1242908d778SJames Bottomley phy->sas_phy.frame_rcvd = &phy->frame_rcvd[0];
1252908d778SJames Bottomley phy->sas_phy.ha = &asd_ha->sas_ha;
1262908d778SJames Bottomley phy->sas_phy.lldd_phy = phy;
1272908d778SJames Bottomley }
1282908d778SJames Bottomley
1292908d778SJames Bottomley /* Now enable and initialize only the enabled phys. */
1302908d778SJames Bottomley for_each_phy(phy_mask, phy_mask, i) {
1312908d778SJames Bottomley int err = asd_init_phy(&asd_ha->phys[i]);
1322908d778SJames Bottomley if (err)
1332908d778SJames Bottomley return err;
1342908d778SJames Bottomley }
1352908d778SJames Bottomley
1362908d778SJames Bottomley return 0;
1372908d778SJames Bottomley }
1382908d778SJames Bottomley
1392908d778SJames Bottomley /* ---------- Sliding windows ---------- */
1402908d778SJames Bottomley
asd_init_sw(struct asd_ha_struct * asd_ha)1412908d778SJames Bottomley static int asd_init_sw(struct asd_ha_struct *asd_ha)
1422908d778SJames Bottomley {
1432908d778SJames Bottomley struct pci_dev *pcidev = asd_ha->pcidev;
1442908d778SJames Bottomley int err;
1452908d778SJames Bottomley u32 v;
1462908d778SJames Bottomley
1472908d778SJames Bottomley /* Unlock MBARs */
1482908d778SJames Bottomley err = pci_read_config_dword(pcidev, PCI_CONF_MBAR_KEY, &v);
1492908d778SJames Bottomley if (err) {
1502908d778SJames Bottomley asd_printk("couldn't access conf. space of %s\n",
1512908d778SJames Bottomley pci_name(pcidev));
1522908d778SJames Bottomley goto Err;
1532908d778SJames Bottomley }
1542908d778SJames Bottomley if (v)
1552908d778SJames Bottomley err = pci_write_config_dword(pcidev, PCI_CONF_MBAR_KEY, v);
1562908d778SJames Bottomley if (err) {
1572908d778SJames Bottomley asd_printk("couldn't write to MBAR_KEY of %s\n",
1582908d778SJames Bottomley pci_name(pcidev));
1592908d778SJames Bottomley goto Err;
1602908d778SJames Bottomley }
1612908d778SJames Bottomley
1622908d778SJames Bottomley /* Set sliding windows A, B and C to point to proper internal
1632908d778SJames Bottomley * memory regions.
1642908d778SJames Bottomley */
1652908d778SJames Bottomley pci_write_config_dword(pcidev, PCI_CONF_MBAR0_SWA, REG_BASE_ADDR);
1662908d778SJames Bottomley pci_write_config_dword(pcidev, PCI_CONF_MBAR0_SWB,
1672908d778SJames Bottomley REG_BASE_ADDR_CSEQCIO);
1682908d778SJames Bottomley pci_write_config_dword(pcidev, PCI_CONF_MBAR0_SWC, REG_BASE_ADDR_EXSI);
1692908d778SJames Bottomley asd_ha->io_handle[0].swa_base = REG_BASE_ADDR;
1702908d778SJames Bottomley asd_ha->io_handle[0].swb_base = REG_BASE_ADDR_CSEQCIO;
1712908d778SJames Bottomley asd_ha->io_handle[0].swc_base = REG_BASE_ADDR_EXSI;
1722908d778SJames Bottomley MBAR0_SWB_SIZE = asd_ha->io_handle[0].len - 0x80;
1732908d778SJames Bottomley if (!asd_ha->iospace) {
1742908d778SJames Bottomley /* MBAR1 will point to OCM (On Chip Memory) */
1752908d778SJames Bottomley pci_write_config_dword(pcidev, PCI_CONF_MBAR1, OCM_BASE_ADDR);
1762908d778SJames Bottomley asd_ha->io_handle[1].swa_base = OCM_BASE_ADDR;
1772908d778SJames Bottomley }
1782908d778SJames Bottomley spin_lock_init(&asd_ha->iolock);
1792908d778SJames Bottomley Err:
1802908d778SJames Bottomley return err;
1812908d778SJames Bottomley }
1822908d778SJames Bottomley
1832908d778SJames Bottomley /* ---------- SCB initialization ---------- */
1842908d778SJames Bottomley
1852908d778SJames Bottomley /**
1862908d778SJames Bottomley * asd_init_scbs - manually allocate the first SCB.
1872908d778SJames Bottomley * @asd_ha: pointer to host adapter structure
1882908d778SJames Bottomley *
1892908d778SJames Bottomley * This allocates the very first SCB which would be sent to the
1902908d778SJames Bottomley * sequencer for execution. Its bus address is written to
1912908d778SJames Bottomley * CSEQ_Q_NEW_POINTER, mode page 2, mode 8. Since the bus address of
1922908d778SJames Bottomley * the _next_ scb to be DMA-ed to the host adapter is read from the last
1932908d778SJames Bottomley * SCB DMA-ed to the host adapter, we have to always stay one step
1942908d778SJames Bottomley * ahead of the sequencer and keep one SCB already allocated.
1952908d778SJames Bottomley */
asd_init_scbs(struct asd_ha_struct * asd_ha)1962908d778SJames Bottomley static int asd_init_scbs(struct asd_ha_struct *asd_ha)
1972908d778SJames Bottomley {
1982908d778SJames Bottomley struct asd_seq_data *seq = &asd_ha->seq;
1992908d778SJames Bottomley int bitmap_bytes;
2002908d778SJames Bottomley
2012908d778SJames Bottomley /* allocate the index array and bitmap */
2022908d778SJames Bottomley asd_ha->seq.tc_index_bitmap_bits = asd_ha->hw_prof.max_scbs;
2036396bb22SKees Cook asd_ha->seq.tc_index_array = kcalloc(asd_ha->seq.tc_index_bitmap_bits,
2046396bb22SKees Cook sizeof(void *),
2056396bb22SKees Cook GFP_KERNEL);
2062908d778SJames Bottomley if (!asd_ha->seq.tc_index_array)
2072908d778SJames Bottomley return -ENOMEM;
2082908d778SJames Bottomley
2092908d778SJames Bottomley bitmap_bytes = (asd_ha->seq.tc_index_bitmap_bits+7)/8;
2102908d778SJames Bottomley bitmap_bytes = BITS_TO_LONGS(bitmap_bytes*8)*sizeof(unsigned long);
2112908d778SJames Bottomley asd_ha->seq.tc_index_bitmap = kzalloc(bitmap_bytes, GFP_KERNEL);
212aa8c65a4SQuentin Lambert if (!asd_ha->seq.tc_index_bitmap) {
213aa8c65a4SQuentin Lambert kfree(asd_ha->seq.tc_index_array);
214aa8c65a4SQuentin Lambert asd_ha->seq.tc_index_array = NULL;
2152908d778SJames Bottomley return -ENOMEM;
216aa8c65a4SQuentin Lambert }
2172908d778SJames Bottomley
2182908d778SJames Bottomley spin_lock_init(&seq->tc_index_lock);
2192908d778SJames Bottomley
2202908d778SJames Bottomley seq->next_scb.size = sizeof(struct scb);
2212908d778SJames Bottomley seq->next_scb.vaddr = dma_pool_alloc(asd_ha->scb_pool, GFP_KERNEL,
2222908d778SJames Bottomley &seq->next_scb.dma_handle);
2232908d778SJames Bottomley if (!seq->next_scb.vaddr) {
2242908d778SJames Bottomley kfree(asd_ha->seq.tc_index_bitmap);
2252908d778SJames Bottomley kfree(asd_ha->seq.tc_index_array);
2262908d778SJames Bottomley asd_ha->seq.tc_index_bitmap = NULL;
2272908d778SJames Bottomley asd_ha->seq.tc_index_array = NULL;
2282908d778SJames Bottomley return -ENOMEM;
2292908d778SJames Bottomley }
2302908d778SJames Bottomley
2312908d778SJames Bottomley seq->pending = 0;
2322908d778SJames Bottomley spin_lock_init(&seq->pend_q_lock);
2332908d778SJames Bottomley INIT_LIST_HEAD(&seq->pend_q);
2342908d778SJames Bottomley
2352908d778SJames Bottomley return 0;
2362908d778SJames Bottomley }
2372908d778SJames Bottomley
asd_get_max_scb_ddb(struct asd_ha_struct * asd_ha)23881e56dedSAdrian Bunk static void asd_get_max_scb_ddb(struct asd_ha_struct *asd_ha)
2392908d778SJames Bottomley {
2402908d778SJames Bottomley asd_ha->hw_prof.max_scbs = asd_get_cmdctx_size(asd_ha)/ASD_SCB_SIZE;
2412908d778SJames Bottomley asd_ha->hw_prof.max_ddbs = asd_get_devctx_size(asd_ha)/ASD_DDB_SIZE;
2422908d778SJames Bottomley ASD_DPRINTK("max_scbs:%d, max_ddbs:%d\n",
2432908d778SJames Bottomley asd_ha->hw_prof.max_scbs,
2442908d778SJames Bottomley asd_ha->hw_prof.max_ddbs);
2452908d778SJames Bottomley }
2462908d778SJames Bottomley
2472908d778SJames Bottomley /* ---------- Done List initialization ---------- */
2482908d778SJames Bottomley
2492908d778SJames Bottomley static void asd_dl_tasklet_handler(unsigned long);
2502908d778SJames Bottomley
asd_init_dl(struct asd_ha_struct * asd_ha)2512908d778SJames Bottomley static int asd_init_dl(struct asd_ha_struct *asd_ha)
2522908d778SJames Bottomley {
2532908d778SJames Bottomley asd_ha->seq.actual_dl
2542908d778SJames Bottomley = asd_alloc_coherent(asd_ha,
2552908d778SJames Bottomley ASD_DL_SIZE * sizeof(struct done_list_struct),
2562908d778SJames Bottomley GFP_KERNEL);
2572908d778SJames Bottomley if (!asd_ha->seq.actual_dl)
2582908d778SJames Bottomley return -ENOMEM;
2592908d778SJames Bottomley asd_ha->seq.dl = asd_ha->seq.actual_dl->vaddr;
2602908d778SJames Bottomley asd_ha->seq.dl_toggle = ASD_DEF_DL_TOGGLE;
2612908d778SJames Bottomley asd_ha->seq.dl_next = 0;
2622908d778SJames Bottomley tasklet_init(&asd_ha->seq.dl_tasklet, asd_dl_tasklet_handler,
2632908d778SJames Bottomley (unsigned long) asd_ha);
2642908d778SJames Bottomley
2652908d778SJames Bottomley return 0;
2662908d778SJames Bottomley }
2672908d778SJames Bottomley
2682908d778SJames Bottomley /* ---------- EDB and ESCB init ---------- */
2692908d778SJames Bottomley
asd_alloc_edbs(struct asd_ha_struct * asd_ha,gfp_t gfp_flags)2703cc27547SAl Viro static int asd_alloc_edbs(struct asd_ha_struct *asd_ha, gfp_t gfp_flags)
2712908d778SJames Bottomley {
2722908d778SJames Bottomley struct asd_seq_data *seq = &asd_ha->seq;
2732908d778SJames Bottomley int i;
2742908d778SJames Bottomley
2756da2ec56SKees Cook seq->edb_arr = kmalloc_array(seq->num_edbs, sizeof(*seq->edb_arr),
2766da2ec56SKees Cook gfp_flags);
2772908d778SJames Bottomley if (!seq->edb_arr)
2782908d778SJames Bottomley return -ENOMEM;
2792908d778SJames Bottomley
2802908d778SJames Bottomley for (i = 0; i < seq->num_edbs; i++) {
2812908d778SJames Bottomley seq->edb_arr[i] = asd_alloc_coherent(asd_ha, ASD_EDB_SIZE,
2822908d778SJames Bottomley gfp_flags);
2832908d778SJames Bottomley if (!seq->edb_arr[i])
2842908d778SJames Bottomley goto Err_unroll;
2852908d778SJames Bottomley memset(seq->edb_arr[i]->vaddr, 0, ASD_EDB_SIZE);
2862908d778SJames Bottomley }
2872908d778SJames Bottomley
2882908d778SJames Bottomley ASD_DPRINTK("num_edbs:%d\n", seq->num_edbs);
2892908d778SJames Bottomley
2902908d778SJames Bottomley return 0;
2912908d778SJames Bottomley
2922908d778SJames Bottomley Err_unroll:
2932908d778SJames Bottomley for (i-- ; i >= 0; i--)
2942908d778SJames Bottomley asd_free_coherent(asd_ha, seq->edb_arr[i]);
2952908d778SJames Bottomley kfree(seq->edb_arr);
2962908d778SJames Bottomley seq->edb_arr = NULL;
2972908d778SJames Bottomley
2982908d778SJames Bottomley return -ENOMEM;
2992908d778SJames Bottomley }
3002908d778SJames Bottomley
asd_alloc_escbs(struct asd_ha_struct * asd_ha,gfp_t gfp_flags)3012908d778SJames Bottomley static int asd_alloc_escbs(struct asd_ha_struct *asd_ha,
3023cc27547SAl Viro gfp_t gfp_flags)
3032908d778SJames Bottomley {
3042908d778SJames Bottomley struct asd_seq_data *seq = &asd_ha->seq;
3052908d778SJames Bottomley struct asd_ascb *escb;
3062908d778SJames Bottomley int i, escbs;
3072908d778SJames Bottomley
3086da2ec56SKees Cook seq->escb_arr = kmalloc_array(seq->num_escbs, sizeof(*seq->escb_arr),
3092908d778SJames Bottomley gfp_flags);
3102908d778SJames Bottomley if (!seq->escb_arr)
3112908d778SJames Bottomley return -ENOMEM;
3122908d778SJames Bottomley
3132908d778SJames Bottomley escbs = seq->num_escbs;
3142908d778SJames Bottomley escb = asd_ascb_alloc_list(asd_ha, &escbs, gfp_flags);
3152908d778SJames Bottomley if (!escb) {
3162908d778SJames Bottomley asd_printk("couldn't allocate list of escbs\n");
3172908d778SJames Bottomley goto Err;
3182908d778SJames Bottomley }
3192908d778SJames Bottomley seq->num_escbs -= escbs; /* subtract what was not allocated */
3202908d778SJames Bottomley ASD_DPRINTK("num_escbs:%d\n", seq->num_escbs);
3212908d778SJames Bottomley
3222908d778SJames Bottomley for (i = 0; i < seq->num_escbs; i++, escb = list_entry(escb->list.next,
3232908d778SJames Bottomley struct asd_ascb,
3242908d778SJames Bottomley list)) {
3252908d778SJames Bottomley seq->escb_arr[i] = escb;
3262908d778SJames Bottomley escb->scb->header.opcode = EMPTY_SCB;
3272908d778SJames Bottomley }
3282908d778SJames Bottomley
3292908d778SJames Bottomley return 0;
3302908d778SJames Bottomley Err:
3312908d778SJames Bottomley kfree(seq->escb_arr);
3322908d778SJames Bottomley seq->escb_arr = NULL;
3332908d778SJames Bottomley return -ENOMEM;
3342908d778SJames Bottomley
3352908d778SJames Bottomley }
3362908d778SJames Bottomley
asd_assign_edbs2escbs(struct asd_ha_struct * asd_ha)3372908d778SJames Bottomley static void asd_assign_edbs2escbs(struct asd_ha_struct *asd_ha)
3382908d778SJames Bottomley {
3392908d778SJames Bottomley struct asd_seq_data *seq = &asd_ha->seq;
3402908d778SJames Bottomley int i, k, z = 0;
3412908d778SJames Bottomley
3422908d778SJames Bottomley for (i = 0; i < seq->num_escbs; i++) {
3432908d778SJames Bottomley struct asd_ascb *ascb = seq->escb_arr[i];
3442908d778SJames Bottomley struct empty_scb *escb = &ascb->scb->escb;
3452908d778SJames Bottomley
3462908d778SJames Bottomley ascb->edb_index = z;
3472908d778SJames Bottomley
3482908d778SJames Bottomley escb->num_valid = ASD_EDBS_PER_SCB;
3492908d778SJames Bottomley
3502908d778SJames Bottomley for (k = 0; k < ASD_EDBS_PER_SCB; k++) {
3512908d778SJames Bottomley struct sg_el *eb = &escb->eb[k];
3522908d778SJames Bottomley struct asd_dma_tok *edb = seq->edb_arr[z++];
3532908d778SJames Bottomley
3542908d778SJames Bottomley memset(eb, 0, sizeof(*eb));
3552908d778SJames Bottomley eb->bus_addr = cpu_to_le64(((u64) edb->dma_handle));
3562908d778SJames Bottomley eb->size = cpu_to_le32(((u32) edb->size));
3572908d778SJames Bottomley }
3582908d778SJames Bottomley }
3592908d778SJames Bottomley }
3602908d778SJames Bottomley
3612908d778SJames Bottomley /**
3622908d778SJames Bottomley * asd_init_escbs -- allocate and initialize empty scbs
3632908d778SJames Bottomley * @asd_ha: pointer to host adapter structure
3642908d778SJames Bottomley *
3652908d778SJames Bottomley * An empty SCB has sg_elements of ASD_EDBS_PER_SCB (7) buffers.
3662908d778SJames Bottomley * They transport sense data, etc.
3672908d778SJames Bottomley */
asd_init_escbs(struct asd_ha_struct * asd_ha)3682908d778SJames Bottomley static int asd_init_escbs(struct asd_ha_struct *asd_ha)
3692908d778SJames Bottomley {
3702908d778SJames Bottomley struct asd_seq_data *seq = &asd_ha->seq;
3712908d778SJames Bottomley int err = 0;
3722908d778SJames Bottomley
3732908d778SJames Bottomley /* Allocate two empty data buffers (edb) per sequencer. */
3742908d778SJames Bottomley int edbs = 2*(1+asd_ha->hw_prof.num_phys);
3752908d778SJames Bottomley
3762908d778SJames Bottomley seq->num_escbs = (edbs+ASD_EDBS_PER_SCB-1)/ASD_EDBS_PER_SCB;
3772908d778SJames Bottomley seq->num_edbs = seq->num_escbs * ASD_EDBS_PER_SCB;
3782908d778SJames Bottomley
3792908d778SJames Bottomley err = asd_alloc_edbs(asd_ha, GFP_KERNEL);
3802908d778SJames Bottomley if (err) {
3812908d778SJames Bottomley asd_printk("couldn't allocate edbs\n");
3822908d778SJames Bottomley return err;
3832908d778SJames Bottomley }
3842908d778SJames Bottomley
3852908d778SJames Bottomley err = asd_alloc_escbs(asd_ha, GFP_KERNEL);
3862908d778SJames Bottomley if (err) {
3872908d778SJames Bottomley asd_printk("couldn't allocate escbs\n");
3882908d778SJames Bottomley return err;
3892908d778SJames Bottomley }
3902908d778SJames Bottomley
3912908d778SJames Bottomley asd_assign_edbs2escbs(asd_ha);
3922908d778SJames Bottomley /* In order to insure that normal SCBs do not overfill sequencer
3932908d778SJames Bottomley * memory and leave no space for escbs (halting condition),
3942908d778SJames Bottomley * we increment pending here by the number of escbs. However,
3952908d778SJames Bottomley * escbs are never pending.
3962908d778SJames Bottomley */
3972908d778SJames Bottomley seq->pending = seq->num_escbs;
3982908d778SJames Bottomley seq->can_queue = 1 + (asd_ha->hw_prof.max_scbs - seq->pending)/2;
3992908d778SJames Bottomley
4002908d778SJames Bottomley return 0;
4012908d778SJames Bottomley }
4022908d778SJames Bottomley
4032908d778SJames Bottomley /* ---------- HW initialization ---------- */
4042908d778SJames Bottomley
4052908d778SJames Bottomley /**
4062908d778SJames Bottomley * asd_chip_hardrst -- hard reset the chip
4072908d778SJames Bottomley * @asd_ha: pointer to host adapter structure
4082908d778SJames Bottomley *
4092908d778SJames Bottomley * This takes 16 cycles and is synchronous to CFCLK, which runs
4102908d778SJames Bottomley * at 200 MHz, so this should take at most 80 nanoseconds.
4112908d778SJames Bottomley */
asd_chip_hardrst(struct asd_ha_struct * asd_ha)4122908d778SJames Bottomley int asd_chip_hardrst(struct asd_ha_struct *asd_ha)
4132908d778SJames Bottomley {
4142908d778SJames Bottomley int i;
4152908d778SJames Bottomley int count = 100;
4162908d778SJames Bottomley u32 reg;
4172908d778SJames Bottomley
4182908d778SJames Bottomley for (i = 0 ; i < 4 ; i++) {
4192908d778SJames Bottomley asd_write_reg_dword(asd_ha, COMBIST, HARDRST);
4202908d778SJames Bottomley }
4212908d778SJames Bottomley
4222908d778SJames Bottomley do {
4232908d778SJames Bottomley udelay(1);
4242908d778SJames Bottomley reg = asd_read_reg_dword(asd_ha, CHIMINT);
4252908d778SJames Bottomley if (reg & HARDRSTDET) {
4262908d778SJames Bottomley asd_write_reg_dword(asd_ha, CHIMINT,
4272908d778SJames Bottomley HARDRSTDET|PORRSTDET);
4282908d778SJames Bottomley return 0;
4292908d778SJames Bottomley }
4302908d778SJames Bottomley } while (--count > 0);
4312908d778SJames Bottomley
4322908d778SJames Bottomley return -ENODEV;
4332908d778SJames Bottomley }
4342908d778SJames Bottomley
4352908d778SJames Bottomley /**
4362908d778SJames Bottomley * asd_init_chip -- initialize the chip
4372908d778SJames Bottomley * @asd_ha: pointer to host adapter structure
4382908d778SJames Bottomley *
4392908d778SJames Bottomley * Hard resets the chip, disables HA interrupts, downloads the sequnecer
4402908d778SJames Bottomley * microcode and starts the sequencers. The caller has to explicitly
4412908d778SJames Bottomley * enable HA interrupts with asd_enable_ints(asd_ha).
4422908d778SJames Bottomley */
asd_init_chip(struct asd_ha_struct * asd_ha)4432908d778SJames Bottomley static int asd_init_chip(struct asd_ha_struct *asd_ha)
4442908d778SJames Bottomley {
4452908d778SJames Bottomley int err;
4462908d778SJames Bottomley
4472908d778SJames Bottomley err = asd_chip_hardrst(asd_ha);
4482908d778SJames Bottomley if (err) {
4492908d778SJames Bottomley asd_printk("couldn't hard reset %s\n",
4502908d778SJames Bottomley pci_name(asd_ha->pcidev));
4512908d778SJames Bottomley goto out;
4522908d778SJames Bottomley }
4532908d778SJames Bottomley
4542908d778SJames Bottomley asd_disable_ints(asd_ha);
4552908d778SJames Bottomley
4562908d778SJames Bottomley err = asd_init_seqs(asd_ha);
4572908d778SJames Bottomley if (err) {
4582908d778SJames Bottomley asd_printk("couldn't init seqs for %s\n",
4592908d778SJames Bottomley pci_name(asd_ha->pcidev));
4602908d778SJames Bottomley goto out;
4612908d778SJames Bottomley }
4622908d778SJames Bottomley
4632908d778SJames Bottomley err = asd_start_seqs(asd_ha);
4642908d778SJames Bottomley if (err) {
465c19ca6cbSMasanari Iida asd_printk("couldn't start seqs for %s\n",
4662908d778SJames Bottomley pci_name(asd_ha->pcidev));
4672908d778SJames Bottomley goto out;
4682908d778SJames Bottomley }
4692908d778SJames Bottomley out:
4702908d778SJames Bottomley return err;
4712908d778SJames Bottomley }
4722908d778SJames Bottomley
4732908d778SJames Bottomley #define MAX_DEVS ((OCM_MAX_SIZE) / (ASD_DDB_SIZE))
4742908d778SJames Bottomley
4752908d778SJames Bottomley static int max_devs = 0;
4762908d778SJames Bottomley module_param_named(max_devs, max_devs, int, S_IRUGO);
4772908d778SJames Bottomley MODULE_PARM_DESC(max_devs, "\n"
4782908d778SJames Bottomley "\tMaximum number of SAS devices to support (not LUs).\n"
4792908d778SJames Bottomley "\tDefault: 2176, Maximum: 65663.\n");
4802908d778SJames Bottomley
4812908d778SJames Bottomley static int max_cmnds = 0;
4822908d778SJames Bottomley module_param_named(max_cmnds, max_cmnds, int, S_IRUGO);
4832908d778SJames Bottomley MODULE_PARM_DESC(max_cmnds, "\n"
4842908d778SJames Bottomley "\tMaximum number of commands queuable.\n"
4852908d778SJames Bottomley "\tDefault: 512, Maximum: 66047.\n");
4862908d778SJames Bottomley
asd_extend_devctx_ocm(struct asd_ha_struct * asd_ha)4872908d778SJames Bottomley static void asd_extend_devctx_ocm(struct asd_ha_struct *asd_ha)
4882908d778SJames Bottomley {
4892908d778SJames Bottomley unsigned long dma_addr = OCM_BASE_ADDR;
4902908d778SJames Bottomley u32 d;
4912908d778SJames Bottomley
4922908d778SJames Bottomley dma_addr -= asd_ha->hw_prof.max_ddbs * ASD_DDB_SIZE;
4932908d778SJames Bottomley asd_write_reg_addr(asd_ha, DEVCTXBASE, (dma_addr_t) dma_addr);
4942908d778SJames Bottomley d = asd_read_reg_dword(asd_ha, CTXDOMAIN);
4952908d778SJames Bottomley d |= 4;
4962908d778SJames Bottomley asd_write_reg_dword(asd_ha, CTXDOMAIN, d);
4972908d778SJames Bottomley asd_ha->hw_prof.max_ddbs += MAX_DEVS;
4982908d778SJames Bottomley }
4992908d778SJames Bottomley
asd_extend_devctx(struct asd_ha_struct * asd_ha)5002908d778SJames Bottomley static int asd_extend_devctx(struct asd_ha_struct *asd_ha)
5012908d778SJames Bottomley {
5022908d778SJames Bottomley dma_addr_t dma_handle;
5032908d778SJames Bottomley unsigned long dma_addr;
5042908d778SJames Bottomley u32 d;
5052908d778SJames Bottomley int size;
5062908d778SJames Bottomley
5072908d778SJames Bottomley asd_extend_devctx_ocm(asd_ha);
5082908d778SJames Bottomley
5092908d778SJames Bottomley asd_ha->hw_prof.ddb_ext = NULL;
5102908d778SJames Bottomley if (max_devs <= asd_ha->hw_prof.max_ddbs || max_devs > 0xFFFF) {
5112908d778SJames Bottomley max_devs = asd_ha->hw_prof.max_ddbs;
5122908d778SJames Bottomley return 0;
5132908d778SJames Bottomley }
5142908d778SJames Bottomley
5152908d778SJames Bottomley size = (max_devs - asd_ha->hw_prof.max_ddbs + 1) * ASD_DDB_SIZE;
5162908d778SJames Bottomley
5172908d778SJames Bottomley asd_ha->hw_prof.ddb_ext = asd_alloc_coherent(asd_ha, size, GFP_KERNEL);
5182908d778SJames Bottomley if (!asd_ha->hw_prof.ddb_ext) {
5192908d778SJames Bottomley asd_printk("couldn't allocate memory for %d devices\n",
5202908d778SJames Bottomley max_devs);
5212908d778SJames Bottomley max_devs = asd_ha->hw_prof.max_ddbs;
5222908d778SJames Bottomley return -ENOMEM;
5232908d778SJames Bottomley }
5242908d778SJames Bottomley dma_handle = asd_ha->hw_prof.ddb_ext->dma_handle;
5252908d778SJames Bottomley dma_addr = ALIGN((unsigned long) dma_handle, ASD_DDB_SIZE);
5262908d778SJames Bottomley dma_addr -= asd_ha->hw_prof.max_ddbs * ASD_DDB_SIZE;
5272908d778SJames Bottomley dma_handle = (dma_addr_t) dma_addr;
5282908d778SJames Bottomley asd_write_reg_addr(asd_ha, DEVCTXBASE, dma_handle);
5292908d778SJames Bottomley d = asd_read_reg_dword(asd_ha, CTXDOMAIN);
5302908d778SJames Bottomley d &= ~4;
5312908d778SJames Bottomley asd_write_reg_dword(asd_ha, CTXDOMAIN, d);
5322908d778SJames Bottomley
5332908d778SJames Bottomley asd_ha->hw_prof.max_ddbs = max_devs;
5342908d778SJames Bottomley
5352908d778SJames Bottomley return 0;
5362908d778SJames Bottomley }
5372908d778SJames Bottomley
asd_extend_cmdctx(struct asd_ha_struct * asd_ha)5382908d778SJames Bottomley static int asd_extend_cmdctx(struct asd_ha_struct *asd_ha)
5392908d778SJames Bottomley {
5402908d778SJames Bottomley dma_addr_t dma_handle;
5412908d778SJames Bottomley unsigned long dma_addr;
5422908d778SJames Bottomley u32 d;
5432908d778SJames Bottomley int size;
5442908d778SJames Bottomley
5452908d778SJames Bottomley asd_ha->hw_prof.scb_ext = NULL;
5462908d778SJames Bottomley if (max_cmnds <= asd_ha->hw_prof.max_scbs || max_cmnds > 0xFFFF) {
5472908d778SJames Bottomley max_cmnds = asd_ha->hw_prof.max_scbs;
5482908d778SJames Bottomley return 0;
5492908d778SJames Bottomley }
5502908d778SJames Bottomley
5512908d778SJames Bottomley size = (max_cmnds - asd_ha->hw_prof.max_scbs + 1) * ASD_SCB_SIZE;
5522908d778SJames Bottomley
5532908d778SJames Bottomley asd_ha->hw_prof.scb_ext = asd_alloc_coherent(asd_ha, size, GFP_KERNEL);
5542908d778SJames Bottomley if (!asd_ha->hw_prof.scb_ext) {
5552908d778SJames Bottomley asd_printk("couldn't allocate memory for %d commands\n",
5562908d778SJames Bottomley max_cmnds);
5572908d778SJames Bottomley max_cmnds = asd_ha->hw_prof.max_scbs;
5582908d778SJames Bottomley return -ENOMEM;
5592908d778SJames Bottomley }
5602908d778SJames Bottomley dma_handle = asd_ha->hw_prof.scb_ext->dma_handle;
5612908d778SJames Bottomley dma_addr = ALIGN((unsigned long) dma_handle, ASD_SCB_SIZE);
5622908d778SJames Bottomley dma_addr -= asd_ha->hw_prof.max_scbs * ASD_SCB_SIZE;
5632908d778SJames Bottomley dma_handle = (dma_addr_t) dma_addr;
5642908d778SJames Bottomley asd_write_reg_addr(asd_ha, CMDCTXBASE, dma_handle);
5652908d778SJames Bottomley d = asd_read_reg_dword(asd_ha, CTXDOMAIN);
5662908d778SJames Bottomley d &= ~1;
5672908d778SJames Bottomley asd_write_reg_dword(asd_ha, CTXDOMAIN, d);
5682908d778SJames Bottomley
5692908d778SJames Bottomley asd_ha->hw_prof.max_scbs = max_cmnds;
5702908d778SJames Bottomley
5712908d778SJames Bottomley return 0;
5722908d778SJames Bottomley }
5732908d778SJames Bottomley
5742908d778SJames Bottomley /**
5752908d778SJames Bottomley * asd_init_ctxmem -- initialize context memory
576bb458974SLee Jones * @asd_ha: pointer to host adapter structure
5772908d778SJames Bottomley *
5782908d778SJames Bottomley * This function sets the maximum number of SCBs and
5792908d778SJames Bottomley * DDBs which can be used by the sequencer. This is normally
5802908d778SJames Bottomley * 512 and 128 respectively. If support for more SCBs or more DDBs
5812908d778SJames Bottomley * is required then CMDCTXBASE, DEVCTXBASE and CTXDOMAIN are
5822908d778SJames Bottomley * initialized here to extend context memory to point to host memory,
5832908d778SJames Bottomley * thus allowing unlimited support for SCBs and DDBs -- only limited
5842908d778SJames Bottomley * by host memory.
5852908d778SJames Bottomley */
asd_init_ctxmem(struct asd_ha_struct * asd_ha)5862908d778SJames Bottomley static int asd_init_ctxmem(struct asd_ha_struct *asd_ha)
5872908d778SJames Bottomley {
5882908d778SJames Bottomley int bitmap_bytes;
5892908d778SJames Bottomley
5902908d778SJames Bottomley asd_get_max_scb_ddb(asd_ha);
5912908d778SJames Bottomley asd_extend_devctx(asd_ha);
5922908d778SJames Bottomley asd_extend_cmdctx(asd_ha);
5932908d778SJames Bottomley
5942908d778SJames Bottomley /* The kernel wants bitmaps to be unsigned long sized. */
5952908d778SJames Bottomley bitmap_bytes = (asd_ha->hw_prof.max_ddbs+7)/8;
5962908d778SJames Bottomley bitmap_bytes = BITS_TO_LONGS(bitmap_bytes*8)*sizeof(unsigned long);
5972908d778SJames Bottomley asd_ha->hw_prof.ddb_bitmap = kzalloc(bitmap_bytes, GFP_KERNEL);
5982908d778SJames Bottomley if (!asd_ha->hw_prof.ddb_bitmap)
5992908d778SJames Bottomley return -ENOMEM;
6002908d778SJames Bottomley spin_lock_init(&asd_ha->hw_prof.ddb_lock);
6012908d778SJames Bottomley
6022908d778SJames Bottomley return 0;
6032908d778SJames Bottomley }
6042908d778SJames Bottomley
asd_init_hw(struct asd_ha_struct * asd_ha)6052908d778SJames Bottomley int asd_init_hw(struct asd_ha_struct *asd_ha)
6062908d778SJames Bottomley {
6072908d778SJames Bottomley int err;
6082908d778SJames Bottomley u32 v;
6092908d778SJames Bottomley
6102908d778SJames Bottomley err = asd_init_sw(asd_ha);
6112908d778SJames Bottomley if (err)
6122908d778SJames Bottomley return err;
6132908d778SJames Bottomley
6142908d778SJames Bottomley err = pci_read_config_dword(asd_ha->pcidev, PCIC_HSTPCIX_CNTRL, &v);
6152908d778SJames Bottomley if (err) {
6162908d778SJames Bottomley asd_printk("couldn't read PCIC_HSTPCIX_CNTRL of %s\n",
6172908d778SJames Bottomley pci_name(asd_ha->pcidev));
6182908d778SJames Bottomley return err;
6192908d778SJames Bottomley }
62039f27574SChristophe JAILLET err = pci_write_config_dword(asd_ha->pcidev, PCIC_HSTPCIX_CNTRL,
6212908d778SJames Bottomley v | SC_TMR_DIS);
6222908d778SJames Bottomley if (err) {
6232908d778SJames Bottomley asd_printk("couldn't disable split completion timer of %s\n",
6242908d778SJames Bottomley pci_name(asd_ha->pcidev));
6252908d778SJames Bottomley return err;
6262908d778SJames Bottomley }
6272908d778SJames Bottomley
6282908d778SJames Bottomley err = asd_read_ocm(asd_ha);
6292908d778SJames Bottomley if (err) {
6302908d778SJames Bottomley asd_printk("couldn't read ocm(%d)\n", err);
6312908d778SJames Bottomley /* While suspicios, it is not an error that we
6322908d778SJames Bottomley * couldn't read the OCM. */
6332908d778SJames Bottomley }
6342908d778SJames Bottomley
6352908d778SJames Bottomley err = asd_read_flash(asd_ha);
6362908d778SJames Bottomley if (err) {
6372908d778SJames Bottomley asd_printk("couldn't read flash(%d)\n", err);
6382908d778SJames Bottomley /* While suspicios, it is not an error that we
6392908d778SJames Bottomley * couldn't read FLASH memory.
6402908d778SJames Bottomley */
6412908d778SJames Bottomley }
6422908d778SJames Bottomley
6432908d778SJames Bottomley asd_init_ctxmem(asd_ha);
6442908d778SJames Bottomley
64568066c3eSDarrick J. Wong if (asd_get_user_sas_addr(asd_ha)) {
6462908d778SJames Bottomley asd_printk("No SAS Address provided for %s\n",
6472908d778SJames Bottomley pci_name(asd_ha->pcidev));
6482908d778SJames Bottomley err = -ENODEV;
6492908d778SJames Bottomley goto Out;
6502908d778SJames Bottomley }
6512908d778SJames Bottomley
6522908d778SJames Bottomley asd_propagate_sas_addr(asd_ha);
6532908d778SJames Bottomley
6542908d778SJames Bottomley err = asd_init_phys(asd_ha);
6552908d778SJames Bottomley if (err) {
6562908d778SJames Bottomley asd_printk("couldn't initialize phys for %s\n",
6572908d778SJames Bottomley pci_name(asd_ha->pcidev));
6582908d778SJames Bottomley goto Out;
6592908d778SJames Bottomley }
6602908d778SJames Bottomley
6613f048109Smalahal@us.ibm.com asd_init_ports(asd_ha);
6623f048109Smalahal@us.ibm.com
6632908d778SJames Bottomley err = asd_init_scbs(asd_ha);
6642908d778SJames Bottomley if (err) {
6652908d778SJames Bottomley asd_printk("couldn't initialize scbs for %s\n",
6662908d778SJames Bottomley pci_name(asd_ha->pcidev));
6672908d778SJames Bottomley goto Out;
6682908d778SJames Bottomley }
6692908d778SJames Bottomley
6702908d778SJames Bottomley err = asd_init_dl(asd_ha);
6712908d778SJames Bottomley if (err) {
6722908d778SJames Bottomley asd_printk("couldn't initialize the done list:%d\n",
6732908d778SJames Bottomley err);
6742908d778SJames Bottomley goto Out;
6752908d778SJames Bottomley }
6762908d778SJames Bottomley
6772908d778SJames Bottomley err = asd_init_escbs(asd_ha);
6782908d778SJames Bottomley if (err) {
6792908d778SJames Bottomley asd_printk("couldn't initialize escbs\n");
6802908d778SJames Bottomley goto Out;
6812908d778SJames Bottomley }
6822908d778SJames Bottomley
6832908d778SJames Bottomley err = asd_init_chip(asd_ha);
6842908d778SJames Bottomley if (err) {
6852908d778SJames Bottomley asd_printk("couldn't init the chip\n");
6862908d778SJames Bottomley goto Out;
6872908d778SJames Bottomley }
6882908d778SJames Bottomley Out:
6892908d778SJames Bottomley return err;
6902908d778SJames Bottomley }
6912908d778SJames Bottomley
6922908d778SJames Bottomley /* ---------- Chip reset ---------- */
6932908d778SJames Bottomley
6942908d778SJames Bottomley /**
6952908d778SJames Bottomley * asd_chip_reset -- reset the host adapter, etc
6962908d778SJames Bottomley * @asd_ha: pointer to host adapter structure of interest
6972908d778SJames Bottomley *
6982908d778SJames Bottomley * Called from the ISR. Hard reset the chip. Let everything
6992908d778SJames Bottomley * timeout. This should be no different than hot-unplugging the
7002908d778SJames Bottomley * host adapter. Once everything times out we'll init the chip with
7012908d778SJames Bottomley * a call to asd_init_chip() and enable interrupts with asd_enable_ints().
7022908d778SJames Bottomley * XXX finish.
7032908d778SJames Bottomley */
asd_chip_reset(struct asd_ha_struct * asd_ha)7042908d778SJames Bottomley static void asd_chip_reset(struct asd_ha_struct *asd_ha)
7052908d778SJames Bottomley {
7062908d778SJames Bottomley ASD_DPRINTK("chip reset for %s\n", pci_name(asd_ha->pcidev));
7072908d778SJames Bottomley asd_chip_hardrst(asd_ha);
7082908d778SJames Bottomley }
7092908d778SJames Bottomley
7102908d778SJames Bottomley /* ---------- Done List Routines ---------- */
7112908d778SJames Bottomley
asd_dl_tasklet_handler(unsigned long data)7122908d778SJames Bottomley static void asd_dl_tasklet_handler(unsigned long data)
7132908d778SJames Bottomley {
7142908d778SJames Bottomley struct asd_ha_struct *asd_ha = (struct asd_ha_struct *) data;
7152908d778SJames Bottomley struct asd_seq_data *seq = &asd_ha->seq;
7162908d778SJames Bottomley unsigned long flags;
7172908d778SJames Bottomley
7182908d778SJames Bottomley while (1) {
7192908d778SJames Bottomley struct done_list_struct *dl = &seq->dl[seq->dl_next];
7202908d778SJames Bottomley struct asd_ascb *ascb;
7212908d778SJames Bottomley
7222908d778SJames Bottomley if ((dl->toggle & DL_TOGGLE_MASK) != seq->dl_toggle)
7232908d778SJames Bottomley break;
7242908d778SJames Bottomley
7252908d778SJames Bottomley /* find the aSCB */
7262908d778SJames Bottomley spin_lock_irqsave(&seq->tc_index_lock, flags);
7272908d778SJames Bottomley ascb = asd_tc_index_find(seq, (int)le16_to_cpu(dl->index));
7282908d778SJames Bottomley spin_unlock_irqrestore(&seq->tc_index_lock, flags);
7292908d778SJames Bottomley if (unlikely(!ascb)) {
7302908d778SJames Bottomley ASD_DPRINTK("BUG:sequencer:dl:no ascb?!\n");
7312908d778SJames Bottomley goto next_1;
7322908d778SJames Bottomley } else if (ascb->scb->header.opcode == EMPTY_SCB) {
7332908d778SJames Bottomley goto out;
7342908d778SJames Bottomley } else if (!ascb->uldd_timer && !del_timer(&ascb->timer)) {
7352908d778SJames Bottomley goto next_1;
7362908d778SJames Bottomley }
7372908d778SJames Bottomley spin_lock_irqsave(&seq->pend_q_lock, flags);
7382908d778SJames Bottomley list_del_init(&ascb->list);
7392908d778SJames Bottomley seq->pending--;
7402908d778SJames Bottomley spin_unlock_irqrestore(&seq->pend_q_lock, flags);
7412908d778SJames Bottomley out:
7422908d778SJames Bottomley ascb->tasklet_complete(ascb, dl);
7432908d778SJames Bottomley
7442908d778SJames Bottomley next_1:
7452908d778SJames Bottomley seq->dl_next = (seq->dl_next + 1) & (ASD_DL_SIZE-1);
7462908d778SJames Bottomley if (!seq->dl_next)
7472908d778SJames Bottomley seq->dl_toggle ^= DL_TOGGLE_MASK;
7482908d778SJames Bottomley }
7492908d778SJames Bottomley }
7502908d778SJames Bottomley
7512908d778SJames Bottomley /* ---------- Interrupt Service Routines ---------- */
7522908d778SJames Bottomley
7532908d778SJames Bottomley /**
7542908d778SJames Bottomley * asd_process_donelist_isr -- schedule processing of done list entries
7552908d778SJames Bottomley * @asd_ha: pointer to host adapter structure
7562908d778SJames Bottomley */
asd_process_donelist_isr(struct asd_ha_struct * asd_ha)75781e56dedSAdrian Bunk static void asd_process_donelist_isr(struct asd_ha_struct *asd_ha)
7582908d778SJames Bottomley {
7592908d778SJames Bottomley tasklet_schedule(&asd_ha->seq.dl_tasklet);
7602908d778SJames Bottomley }
7612908d778SJames Bottomley
7622908d778SJames Bottomley /**
7632908d778SJames Bottomley * asd_com_sas_isr -- process device communication interrupt (COMINT)
7642908d778SJames Bottomley * @asd_ha: pointer to host adapter structure
7652908d778SJames Bottomley */
asd_com_sas_isr(struct asd_ha_struct * asd_ha)76681e56dedSAdrian Bunk static void asd_com_sas_isr(struct asd_ha_struct *asd_ha)
7672908d778SJames Bottomley {
7682908d778SJames Bottomley u32 comstat = asd_read_reg_dword(asd_ha, COMSTAT);
7692908d778SJames Bottomley
7702908d778SJames Bottomley /* clear COMSTAT int */
7712908d778SJames Bottomley asd_write_reg_dword(asd_ha, COMSTAT, 0xFFFFFFFF);
7722908d778SJames Bottomley
7732908d778SJames Bottomley if (comstat & CSBUFPERR) {
7742908d778SJames Bottomley asd_printk("%s: command/status buffer dma parity error\n",
7752908d778SJames Bottomley pci_name(asd_ha->pcidev));
7762908d778SJames Bottomley } else if (comstat & CSERR) {
7772908d778SJames Bottomley int i;
7782908d778SJames Bottomley u32 dmaerr = asd_read_reg_dword(asd_ha, DMAERR);
7792908d778SJames Bottomley dmaerr &= 0xFF;
7802908d778SJames Bottomley asd_printk("%s: command/status dma error, DMAERR: 0x%02x, "
7812908d778SJames Bottomley "CSDMAADR: 0x%04x, CSDMAADR+4: 0x%04x\n",
7822908d778SJames Bottomley pci_name(asd_ha->pcidev),
7832908d778SJames Bottomley dmaerr,
7842908d778SJames Bottomley asd_read_reg_dword(asd_ha, CSDMAADR),
7852908d778SJames Bottomley asd_read_reg_dword(asd_ha, CSDMAADR+4));
7862908d778SJames Bottomley asd_printk("CSBUFFER:\n");
7872908d778SJames Bottomley for (i = 0; i < 8; i++) {
7882908d778SJames Bottomley asd_printk("%08x %08x %08x %08x\n",
7892908d778SJames Bottomley asd_read_reg_dword(asd_ha, CSBUFFER),
7902908d778SJames Bottomley asd_read_reg_dword(asd_ha, CSBUFFER+4),
7912908d778SJames Bottomley asd_read_reg_dword(asd_ha, CSBUFFER+8),
7922908d778SJames Bottomley asd_read_reg_dword(asd_ha, CSBUFFER+12));
7932908d778SJames Bottomley }
7942908d778SJames Bottomley asd_dump_seq_state(asd_ha, 0);
7952908d778SJames Bottomley } else if (comstat & OVLYERR) {
7962908d778SJames Bottomley u32 dmaerr = asd_read_reg_dword(asd_ha, DMAERR);
7972908d778SJames Bottomley dmaerr = (dmaerr >> 8) & 0xFF;
7982908d778SJames Bottomley asd_printk("%s: overlay dma error:0x%x\n",
7992908d778SJames Bottomley pci_name(asd_ha->pcidev),
8002908d778SJames Bottomley dmaerr);
8012908d778SJames Bottomley }
8022908d778SJames Bottomley asd_chip_reset(asd_ha);
8032908d778SJames Bottomley }
8042908d778SJames Bottomley
asd_arp2_err(struct asd_ha_struct * asd_ha,u32 dchstatus)80581e56dedSAdrian Bunk static void asd_arp2_err(struct asd_ha_struct *asd_ha, u32 dchstatus)
8062908d778SJames Bottomley {
8072908d778SJames Bottomley static const char *halt_code[256] = {
8082908d778SJames Bottomley "UNEXPECTED_INTERRUPT0",
8092908d778SJames Bottomley "UNEXPECTED_INTERRUPT1",
8102908d778SJames Bottomley "UNEXPECTED_INTERRUPT2",
8112908d778SJames Bottomley "UNEXPECTED_INTERRUPT3",
8122908d778SJames Bottomley "UNEXPECTED_INTERRUPT4",
8132908d778SJames Bottomley "UNEXPECTED_INTERRUPT5",
8142908d778SJames Bottomley "UNEXPECTED_INTERRUPT6",
8152908d778SJames Bottomley "UNEXPECTED_INTERRUPT7",
8162908d778SJames Bottomley "UNEXPECTED_INTERRUPT8",
8172908d778SJames Bottomley "UNEXPECTED_INTERRUPT9",
8182908d778SJames Bottomley "UNEXPECTED_INTERRUPT10",
8192908d778SJames Bottomley [11 ... 19] = "unknown[11,19]",
8202908d778SJames Bottomley "NO_FREE_SCB_AVAILABLE",
8212908d778SJames Bottomley "INVALID_SCB_OPCODE",
8222908d778SJames Bottomley "INVALID_MBX_OPCODE",
8232908d778SJames Bottomley "INVALID_ATA_STATE",
8242908d778SJames Bottomley "ATA_QUEUE_FULL",
8252908d778SJames Bottomley "ATA_TAG_TABLE_FAULT",
8262908d778SJames Bottomley "ATA_TAG_MASK_FAULT",
8272908d778SJames Bottomley "BAD_LINK_QUEUE_STATE",
8282908d778SJames Bottomley "DMA2CHIM_QUEUE_ERROR",
8292908d778SJames Bottomley "EMPTY_SCB_LIST_FULL",
8302908d778SJames Bottomley "unknown[30]",
8312908d778SJames Bottomley "IN_USE_SCB_ON_FREE_LIST",
8322908d778SJames Bottomley "BAD_OPEN_WAIT_STATE",
8332908d778SJames Bottomley "INVALID_STP_AFFILIATION",
8342908d778SJames Bottomley "unknown[34]",
8352908d778SJames Bottomley "EXEC_QUEUE_ERROR",
8362908d778SJames Bottomley "TOO_MANY_EMPTIES_NEEDED",
8372908d778SJames Bottomley "EMPTY_REQ_QUEUE_ERROR",
8382908d778SJames Bottomley "Q_MONIRTT_MGMT_ERROR",
8392908d778SJames Bottomley "TARGET_MODE_FLOW_ERROR",
8402908d778SJames Bottomley "DEVICE_QUEUE_NOT_FOUND",
8412908d778SJames Bottomley "START_IRTT_TIMER_ERROR",
8422908d778SJames Bottomley "ABORT_TASK_ILLEGAL_REQ",
8432908d778SJames Bottomley [43 ... 255] = "unknown[43,255]"
8442908d778SJames Bottomley };
8452908d778SJames Bottomley
8462908d778SJames Bottomley if (dchstatus & CSEQINT) {
8472908d778SJames Bottomley u32 arp2int = asd_read_reg_dword(asd_ha, CARP2INT);
8482908d778SJames Bottomley
8492908d778SJames Bottomley if (arp2int & (ARP2WAITTO|ARP2ILLOPC|ARP2PERR|ARP2CIOPERR)) {
8502908d778SJames Bottomley asd_printk("%s: CSEQ arp2int:0x%x\n",
8512908d778SJames Bottomley pci_name(asd_ha->pcidev),
8522908d778SJames Bottomley arp2int);
8532908d778SJames Bottomley } else if (arp2int & ARP2HALTC)
8542908d778SJames Bottomley asd_printk("%s: CSEQ halted: %s\n",
8552908d778SJames Bottomley pci_name(asd_ha->pcidev),
8562908d778SJames Bottomley halt_code[(arp2int>>16)&0xFF]);
8572908d778SJames Bottomley else
8582908d778SJames Bottomley asd_printk("%s: CARP2INT:0x%x\n",
8592908d778SJames Bottomley pci_name(asd_ha->pcidev),
8602908d778SJames Bottomley arp2int);
8612908d778SJames Bottomley }
8622908d778SJames Bottomley if (dchstatus & LSEQINT_MASK) {
8632908d778SJames Bottomley int lseq;
8642908d778SJames Bottomley u8 lseq_mask = dchstatus & LSEQINT_MASK;
8652908d778SJames Bottomley
8662908d778SJames Bottomley for_each_sequencer(lseq_mask, lseq_mask, lseq) {
8672908d778SJames Bottomley u32 arp2int = asd_read_reg_dword(asd_ha,
8682908d778SJames Bottomley LmARP2INT(lseq));
8692908d778SJames Bottomley if (arp2int & (ARP2WAITTO | ARP2ILLOPC | ARP2PERR
8702908d778SJames Bottomley | ARP2CIOPERR)) {
8712908d778SJames Bottomley asd_printk("%s: LSEQ%d arp2int:0x%x\n",
8722908d778SJames Bottomley pci_name(asd_ha->pcidev),
8732908d778SJames Bottomley lseq, arp2int);
8742908d778SJames Bottomley /* XXX we should only do lseq reset */
8752908d778SJames Bottomley } else if (arp2int & ARP2HALTC)
8762908d778SJames Bottomley asd_printk("%s: LSEQ%d halted: %s\n",
8772908d778SJames Bottomley pci_name(asd_ha->pcidev),
8782908d778SJames Bottomley lseq,halt_code[(arp2int>>16)&0xFF]);
8792908d778SJames Bottomley else
8802908d778SJames Bottomley asd_printk("%s: LSEQ%d ARP2INT:0x%x\n",
8812908d778SJames Bottomley pci_name(asd_ha->pcidev), lseq,
8822908d778SJames Bottomley arp2int);
8832908d778SJames Bottomley }
8842908d778SJames Bottomley }
8852908d778SJames Bottomley asd_chip_reset(asd_ha);
8862908d778SJames Bottomley }
8872908d778SJames Bottomley
8882908d778SJames Bottomley /**
8892908d778SJames Bottomley * asd_dch_sas_isr -- process device channel interrupt (DEVINT)
8902908d778SJames Bottomley * @asd_ha: pointer to host adapter structure
8912908d778SJames Bottomley */
asd_dch_sas_isr(struct asd_ha_struct * asd_ha)89281e56dedSAdrian Bunk static void asd_dch_sas_isr(struct asd_ha_struct *asd_ha)
8932908d778SJames Bottomley {
8942908d778SJames Bottomley u32 dchstatus = asd_read_reg_dword(asd_ha, DCHSTATUS);
8952908d778SJames Bottomley
8962908d778SJames Bottomley if (dchstatus & CFIFTOERR) {
8972908d778SJames Bottomley asd_printk("%s: CFIFTOERR\n", pci_name(asd_ha->pcidev));
8982908d778SJames Bottomley asd_chip_reset(asd_ha);
8992908d778SJames Bottomley } else
9002908d778SJames Bottomley asd_arp2_err(asd_ha, dchstatus);
9012908d778SJames Bottomley }
9022908d778SJames Bottomley
9032908d778SJames Bottomley /**
904577c65b0SLee Jones * asd_rbi_exsi_isr -- process external system interface interrupt (INITERR)
9052908d778SJames Bottomley * @asd_ha: pointer to host adapter structure
9062908d778SJames Bottomley */
asd_rbi_exsi_isr(struct asd_ha_struct * asd_ha)90781e56dedSAdrian Bunk static void asd_rbi_exsi_isr(struct asd_ha_struct *asd_ha)
9082908d778SJames Bottomley {
9092908d778SJames Bottomley u32 stat0r = asd_read_reg_dword(asd_ha, ASISTAT0R);
9102908d778SJames Bottomley
9112908d778SJames Bottomley if (!(stat0r & ASIERR)) {
9122908d778SJames Bottomley asd_printk("hmm, EXSI interrupted but no error?\n");
9132908d778SJames Bottomley return;
9142908d778SJames Bottomley }
9152908d778SJames Bottomley
9162908d778SJames Bottomley if (stat0r & ASIFMTERR) {
9172908d778SJames Bottomley asd_printk("ASI SEEPROM format error for %s\n",
9182908d778SJames Bottomley pci_name(asd_ha->pcidev));
9192908d778SJames Bottomley } else if (stat0r & ASISEECHKERR) {
9202908d778SJames Bottomley u32 stat1r = asd_read_reg_dword(asd_ha, ASISTAT1R);
9212908d778SJames Bottomley asd_printk("ASI SEEPROM checksum 0x%x error for %s\n",
9222908d778SJames Bottomley stat1r & CHECKSUM_MASK,
9232908d778SJames Bottomley pci_name(asd_ha->pcidev));
9242908d778SJames Bottomley } else {
9252908d778SJames Bottomley u32 statr = asd_read_reg_dword(asd_ha, ASIERRSTATR);
9262908d778SJames Bottomley
9272908d778SJames Bottomley if (!(statr & CPI2ASIMSTERR_MASK)) {
9282908d778SJames Bottomley ASD_DPRINTK("hmm, ASIERR?\n");
9292908d778SJames Bottomley return;
9302908d778SJames Bottomley } else {
9312908d778SJames Bottomley u32 addr = asd_read_reg_dword(asd_ha, ASIERRADDR);
9322908d778SJames Bottomley u32 data = asd_read_reg_dword(asd_ha, ASIERRDATAR);
9332908d778SJames Bottomley
9342908d778SJames Bottomley asd_printk("%s: CPI2 xfer err: addr: 0x%x, wdata: 0x%x, "
9352908d778SJames Bottomley "count: 0x%x, byteen: 0x%x, targerr: 0x%x "
9362908d778SJames Bottomley "master id: 0x%x, master err: 0x%x\n",
9372908d778SJames Bottomley pci_name(asd_ha->pcidev),
9382908d778SJames Bottomley addr, data,
9392908d778SJames Bottomley (statr & CPI2ASIBYTECNT_MASK) >> 16,
9402908d778SJames Bottomley (statr & CPI2ASIBYTEEN_MASK) >> 12,
9412908d778SJames Bottomley (statr & CPI2ASITARGERR_MASK) >> 8,
9422908d778SJames Bottomley (statr & CPI2ASITARGMID_MASK) >> 4,
9432908d778SJames Bottomley (statr & CPI2ASIMSTERR_MASK));
9442908d778SJames Bottomley }
9452908d778SJames Bottomley }
9462908d778SJames Bottomley asd_chip_reset(asd_ha);
9472908d778SJames Bottomley }
9482908d778SJames Bottomley
9492908d778SJames Bottomley /**
9502908d778SJames Bottomley * asd_hst_pcix_isr -- process host interface interrupts
9512908d778SJames Bottomley * @asd_ha: pointer to host adapter structure
9522908d778SJames Bottomley *
9532908d778SJames Bottomley * Asserted on PCIX errors: target abort, etc.
9542908d778SJames Bottomley */
asd_hst_pcix_isr(struct asd_ha_struct * asd_ha)95581e56dedSAdrian Bunk static void asd_hst_pcix_isr(struct asd_ha_struct *asd_ha)
9562908d778SJames Bottomley {
9572908d778SJames Bottomley u16 status;
9582908d778SJames Bottomley u32 pcix_status;
9592908d778SJames Bottomley u32 ecc_status;
9602908d778SJames Bottomley
9612908d778SJames Bottomley pci_read_config_word(asd_ha->pcidev, PCI_STATUS, &status);
9622908d778SJames Bottomley pci_read_config_dword(asd_ha->pcidev, PCIX_STATUS, &pcix_status);
9632908d778SJames Bottomley pci_read_config_dword(asd_ha->pcidev, ECC_CTRL_STAT, &ecc_status);
9642908d778SJames Bottomley
9652908d778SJames Bottomley if (status & PCI_STATUS_DETECTED_PARITY)
9662908d778SJames Bottomley asd_printk("parity error for %s\n", pci_name(asd_ha->pcidev));
9672908d778SJames Bottomley else if (status & PCI_STATUS_REC_MASTER_ABORT)
9682908d778SJames Bottomley asd_printk("master abort for %s\n", pci_name(asd_ha->pcidev));
9692908d778SJames Bottomley else if (status & PCI_STATUS_REC_TARGET_ABORT)
9702908d778SJames Bottomley asd_printk("target abort for %s\n", pci_name(asd_ha->pcidev));
9712908d778SJames Bottomley else if (status & PCI_STATUS_PARITY)
9722908d778SJames Bottomley asd_printk("data parity for %s\n", pci_name(asd_ha->pcidev));
9732908d778SJames Bottomley else if (pcix_status & RCV_SCE) {
9742908d778SJames Bottomley asd_printk("received split completion error for %s\n",
9752908d778SJames Bottomley pci_name(asd_ha->pcidev));
9762908d778SJames Bottomley pci_write_config_dword(asd_ha->pcidev,PCIX_STATUS,pcix_status);
9772908d778SJames Bottomley /* XXX: Abort task? */
9782908d778SJames Bottomley return;
9792908d778SJames Bottomley } else if (pcix_status & UNEXP_SC) {
9802908d778SJames Bottomley asd_printk("unexpected split completion for %s\n",
9812908d778SJames Bottomley pci_name(asd_ha->pcidev));
9822908d778SJames Bottomley pci_write_config_dword(asd_ha->pcidev,PCIX_STATUS,pcix_status);
9832908d778SJames Bottomley /* ignore */
9842908d778SJames Bottomley return;
9852908d778SJames Bottomley } else if (pcix_status & SC_DISCARD)
9862908d778SJames Bottomley asd_printk("split completion discarded for %s\n",
9872908d778SJames Bottomley pci_name(asd_ha->pcidev));
9882908d778SJames Bottomley else if (ecc_status & UNCOR_ECCERR)
9892908d778SJames Bottomley asd_printk("uncorrectable ECC error for %s\n",
9902908d778SJames Bottomley pci_name(asd_ha->pcidev));
9912908d778SJames Bottomley asd_chip_reset(asd_ha);
9922908d778SJames Bottomley }
9932908d778SJames Bottomley
9942908d778SJames Bottomley /**
9952908d778SJames Bottomley * asd_hw_isr -- host adapter interrupt service routine
9962908d778SJames Bottomley * @irq: ignored
9972908d778SJames Bottomley * @dev_id: pointer to host adapter structure
9982908d778SJames Bottomley *
9992908d778SJames Bottomley * The ISR processes done list entries and level 3 error handling.
10002908d778SJames Bottomley */
asd_hw_isr(int irq,void * dev_id)10017d12e780SDavid Howells irqreturn_t asd_hw_isr(int irq, void *dev_id)
10022908d778SJames Bottomley {
10032908d778SJames Bottomley struct asd_ha_struct *asd_ha = dev_id;
10042908d778SJames Bottomley u32 chimint = asd_read_reg_dword(asd_ha, CHIMINT);
10052908d778SJames Bottomley
10062908d778SJames Bottomley if (!chimint)
10072908d778SJames Bottomley return IRQ_NONE;
10082908d778SJames Bottomley
10092908d778SJames Bottomley asd_write_reg_dword(asd_ha, CHIMINT, chimint);
10102908d778SJames Bottomley (void) asd_read_reg_dword(asd_ha, CHIMINT);
10112908d778SJames Bottomley
10122908d778SJames Bottomley if (chimint & DLAVAIL)
10132908d778SJames Bottomley asd_process_donelist_isr(asd_ha);
10142908d778SJames Bottomley if (chimint & COMINT)
10152908d778SJames Bottomley asd_com_sas_isr(asd_ha);
10162908d778SJames Bottomley if (chimint & DEVINT)
10172908d778SJames Bottomley asd_dch_sas_isr(asd_ha);
10182908d778SJames Bottomley if (chimint & INITERR)
10192908d778SJames Bottomley asd_rbi_exsi_isr(asd_ha);
10202908d778SJames Bottomley if (chimint & HOSTERR)
10212908d778SJames Bottomley asd_hst_pcix_isr(asd_ha);
10222908d778SJames Bottomley
10232908d778SJames Bottomley return IRQ_HANDLED;
10242908d778SJames Bottomley }
10252908d778SJames Bottomley
10262908d778SJames Bottomley /* ---------- SCB handling ---------- */
10272908d778SJames Bottomley
asd_ascb_alloc(struct asd_ha_struct * asd_ha,gfp_t gfp_flags)102881e56dedSAdrian Bunk static struct asd_ascb *asd_ascb_alloc(struct asd_ha_struct *asd_ha,
10293cc27547SAl Viro gfp_t gfp_flags)
10302908d778SJames Bottomley {
1031e18b890bSChristoph Lameter extern struct kmem_cache *asd_ascb_cache;
10322908d778SJames Bottomley struct asd_seq_data *seq = &asd_ha->seq;
10332908d778SJames Bottomley struct asd_ascb *ascb;
10342908d778SJames Bottomley unsigned long flags;
10352908d778SJames Bottomley
1036c3762229SRobert P. J. Day ascb = kmem_cache_zalloc(asd_ascb_cache, gfp_flags);
10372908d778SJames Bottomley
10382908d778SJames Bottomley if (ascb) {
10392908d778SJames Bottomley ascb->dma_scb.size = sizeof(struct scb);
10400e55892eSSouptick Joarder ascb->dma_scb.vaddr = dma_pool_zalloc(asd_ha->scb_pool,
10412908d778SJames Bottomley gfp_flags,
10422908d778SJames Bottomley &ascb->dma_scb.dma_handle);
10432908d778SJames Bottomley if (!ascb->dma_scb.vaddr) {
10442908d778SJames Bottomley kmem_cache_free(asd_ascb_cache, ascb);
10452908d778SJames Bottomley return NULL;
10462908d778SJames Bottomley }
10472908d778SJames Bottomley asd_init_ascb(asd_ha, ascb);
10482908d778SJames Bottomley
10492908d778SJames Bottomley spin_lock_irqsave(&seq->tc_index_lock, flags);
10502908d778SJames Bottomley ascb->tc_index = asd_tc_index_get(seq, ascb);
10512908d778SJames Bottomley spin_unlock_irqrestore(&seq->tc_index_lock, flags);
10522908d778SJames Bottomley if (ascb->tc_index == -1)
10532908d778SJames Bottomley goto undo;
10542908d778SJames Bottomley
10552908d778SJames Bottomley ascb->scb->header.index = cpu_to_le16((u16)ascb->tc_index);
10562908d778SJames Bottomley }
10572908d778SJames Bottomley
10582908d778SJames Bottomley return ascb;
10592908d778SJames Bottomley undo:
10602908d778SJames Bottomley dma_pool_free(asd_ha->scb_pool, ascb->dma_scb.vaddr,
10612908d778SJames Bottomley ascb->dma_scb.dma_handle);
10622908d778SJames Bottomley kmem_cache_free(asd_ascb_cache, ascb);
10632908d778SJames Bottomley ASD_DPRINTK("no index for ascb\n");
10642908d778SJames Bottomley return NULL;
10652908d778SJames Bottomley }
10662908d778SJames Bottomley
10672908d778SJames Bottomley /**
10682908d778SJames Bottomley * asd_ascb_alloc_list -- allocate a list of aSCBs
10692908d778SJames Bottomley * @asd_ha: pointer to host adapter structure
10702908d778SJames Bottomley * @num: pointer to integer number of aSCBs
10712908d778SJames Bottomley * @gfp_flags: GFP_ flags.
10722908d778SJames Bottomley *
10732908d778SJames Bottomley * This is the only function which is used to allocate aSCBs.
10742908d778SJames Bottomley * It can allocate one or many. If more than one, then they form
10752908d778SJames Bottomley * a linked list in two ways: by their list field of the ascb struct
10762908d778SJames Bottomley * and by the next_scb field of the scb_header.
10772908d778SJames Bottomley *
10782908d778SJames Bottomley * Returns NULL if no memory was available, else pointer to a list
10792908d778SJames Bottomley * of ascbs. When this function returns, @num would be the number
10802908d778SJames Bottomley * of SCBs which were not able to be allocated, 0 if all requested
10812908d778SJames Bottomley * were able to be allocated.
10822908d778SJames Bottomley */
asd_ascb_alloc_list(struct asd_ha_struct * asd_ha,int * num,gfp_t gfp_flags)10832908d778SJames Bottomley struct asd_ascb *asd_ascb_alloc_list(struct asd_ha_struct
10842908d778SJames Bottomley *asd_ha, int *num,
10853cc27547SAl Viro gfp_t gfp_flags)
10862908d778SJames Bottomley {
10872908d778SJames Bottomley struct asd_ascb *first = NULL;
10882908d778SJames Bottomley
10892908d778SJames Bottomley for ( ; *num > 0; --*num) {
10902908d778SJames Bottomley struct asd_ascb *ascb = asd_ascb_alloc(asd_ha, gfp_flags);
10912908d778SJames Bottomley
10922908d778SJames Bottomley if (!ascb)
10932908d778SJames Bottomley break;
10942908d778SJames Bottomley else if (!first)
10952908d778SJames Bottomley first = ascb;
10962908d778SJames Bottomley else {
10972908d778SJames Bottomley struct asd_ascb *last = list_entry(first->list.prev,
10982908d778SJames Bottomley struct asd_ascb,
10992908d778SJames Bottomley list);
11002908d778SJames Bottomley list_add_tail(&ascb->list, &first->list);
11012908d778SJames Bottomley last->scb->header.next_scb =
11022908d778SJames Bottomley cpu_to_le64(((u64)ascb->dma_scb.dma_handle));
11032908d778SJames Bottomley }
11042908d778SJames Bottomley }
11052908d778SJames Bottomley
11062908d778SJames Bottomley return first;
11072908d778SJames Bottomley }
11082908d778SJames Bottomley
11092908d778SJames Bottomley /**
11102908d778SJames Bottomley * asd_swap_head_scb -- swap the head scb
11112908d778SJames Bottomley * @asd_ha: pointer to host adapter structure
11122908d778SJames Bottomley * @ascb: pointer to the head of an ascb list
11132908d778SJames Bottomley *
11142908d778SJames Bottomley * The sequencer knows the DMA address of the next SCB to be DMAed to
11152908d778SJames Bottomley * the host adapter, from initialization or from the last list DMAed.
11162908d778SJames Bottomley * seq->next_scb keeps the address of this SCB. The sequencer will
11172908d778SJames Bottomley * DMA to the host adapter this list of SCBs. But the head (first
11182908d778SJames Bottomley * element) of this list is not known to the sequencer. Here we swap
11192908d778SJames Bottomley * the head of the list with the known SCB (memcpy()).
11202908d778SJames Bottomley * Only one memcpy() is required per list so it is in our interest
11212908d778SJames Bottomley * to keep the list of SCB as long as possible so that the ratio
11222908d778SJames Bottomley * of number of memcpy calls to the number of SCB DMA-ed is as small
11232908d778SJames Bottomley * as possible.
11242908d778SJames Bottomley *
11252908d778SJames Bottomley * LOCKING: called with the pending list lock held.
11262908d778SJames Bottomley */
asd_swap_head_scb(struct asd_ha_struct * asd_ha,struct asd_ascb * ascb)112781e56dedSAdrian Bunk static void asd_swap_head_scb(struct asd_ha_struct *asd_ha,
11282908d778SJames Bottomley struct asd_ascb *ascb)
11292908d778SJames Bottomley {
11302908d778SJames Bottomley struct asd_seq_data *seq = &asd_ha->seq;
11312908d778SJames Bottomley struct asd_ascb *last = list_entry(ascb->list.prev,
11322908d778SJames Bottomley struct asd_ascb,
11332908d778SJames Bottomley list);
11342908d778SJames Bottomley struct asd_dma_tok t = ascb->dma_scb;
11352908d778SJames Bottomley
11362908d778SJames Bottomley memcpy(seq->next_scb.vaddr, ascb->scb, sizeof(*ascb->scb));
11372908d778SJames Bottomley ascb->dma_scb = seq->next_scb;
11382908d778SJames Bottomley ascb->scb = ascb->dma_scb.vaddr;
11392908d778SJames Bottomley seq->next_scb = t;
11402908d778SJames Bottomley last->scb->header.next_scb =
11412908d778SJames Bottomley cpu_to_le64(((u64)seq->next_scb.dma_handle));
11422908d778SJames Bottomley }
11432908d778SJames Bottomley
11442908d778SJames Bottomley /**
1145577c65b0SLee Jones * asd_start_scb_timers -- (add and) start timers of SCBs
11462908d778SJames Bottomley * @list: pointer to struct list_head of the scbs
11472908d778SJames Bottomley *
11482908d778SJames Bottomley * If an SCB in the @list has no timer function, assign the default
11492908d778SJames Bottomley * one, then start the timer of the SCB. This function is
11502908d778SJames Bottomley * intended to be called from asd_post_ascb_list(), just prior to
11512908d778SJames Bottomley * posting the SCBs to the sequencer.
11522908d778SJames Bottomley */
asd_start_scb_timers(struct list_head * list)115381e56dedSAdrian Bunk static void asd_start_scb_timers(struct list_head *list)
11542908d778SJames Bottomley {
11552908d778SJames Bottomley struct asd_ascb *ascb;
11562908d778SJames Bottomley list_for_each_entry(ascb, list, list) {
11572908d778SJames Bottomley if (!ascb->uldd_timer) {
1158841b86f3SKees Cook ascb->timer.function = asd_ascb_timedout;
11592908d778SJames Bottomley ascb->timer.expires = jiffies + AIC94XX_SCB_TIMEOUT;
11602908d778SJames Bottomley add_timer(&ascb->timer);
11612908d778SJames Bottomley }
11622908d778SJames Bottomley }
11632908d778SJames Bottomley }
11642908d778SJames Bottomley
11652908d778SJames Bottomley /**
11662908d778SJames Bottomley * asd_post_ascb_list -- post a list of 1 or more aSCBs to the host adapter
11672908d778SJames Bottomley * @asd_ha: pointer to a host adapter structure
11682908d778SJames Bottomley * @ascb: pointer to the first aSCB in the list
11692908d778SJames Bottomley * @num: number of aSCBs in the list (to be posted)
11702908d778SJames Bottomley *
11712908d778SJames Bottomley * See queueing comment in asd_post_escb_list().
11722908d778SJames Bottomley *
11732908d778SJames Bottomley * Additional note on queuing: In order to minimize the ratio of memcpy()
11742908d778SJames Bottomley * to the number of ascbs sent, we try to batch-send as many ascbs as possible
11752908d778SJames Bottomley * in one go.
11762908d778SJames Bottomley * Two cases are possible:
11772908d778SJames Bottomley * A) can_queue >= num,
11782908d778SJames Bottomley * B) can_queue < num.
11792908d778SJames Bottomley * Case A: we can send the whole batch at once. Increment "pending"
11802908d778SJames Bottomley * in the beginning of this function, when it is checked, in order to
11812908d778SJames Bottomley * eliminate races when this function is called by multiple processes.
118279855d17SChristoph Hellwig * Case B: should never happen.
11832908d778SJames Bottomley */
asd_post_ascb_list(struct asd_ha_struct * asd_ha,struct asd_ascb * ascb,int num)11842908d778SJames Bottomley int asd_post_ascb_list(struct asd_ha_struct *asd_ha, struct asd_ascb *ascb,
11852908d778SJames Bottomley int num)
11862908d778SJames Bottomley {
11872908d778SJames Bottomley unsigned long flags;
11882908d778SJames Bottomley LIST_HEAD(list);
11892908d778SJames Bottomley int can_queue;
11902908d778SJames Bottomley
11912908d778SJames Bottomley spin_lock_irqsave(&asd_ha->seq.pend_q_lock, flags);
11922908d778SJames Bottomley can_queue = asd_ha->hw_prof.max_scbs - asd_ha->seq.pending;
11932908d778SJames Bottomley if (can_queue >= num)
11942908d778SJames Bottomley asd_ha->seq.pending += num;
11952908d778SJames Bottomley else
11962908d778SJames Bottomley can_queue = 0;
11972908d778SJames Bottomley
11982908d778SJames Bottomley if (!can_queue) {
11992908d778SJames Bottomley spin_unlock_irqrestore(&asd_ha->seq.pend_q_lock, flags);
12002908d778SJames Bottomley asd_printk("%s: scb queue full\n", pci_name(asd_ha->pcidev));
12012908d778SJames Bottomley return -SAS_QUEUE_FULL;
12022908d778SJames Bottomley }
12032908d778SJames Bottomley
12042908d778SJames Bottomley asd_swap_head_scb(asd_ha, ascb);
12052908d778SJames Bottomley
12062908d778SJames Bottomley __list_add(&list, ascb->list.prev, &ascb->list);
12072908d778SJames Bottomley
12082908d778SJames Bottomley asd_start_scb_timers(&list);
12092908d778SJames Bottomley
12102908d778SJames Bottomley asd_ha->seq.scbpro += num;
12112908d778SJames Bottomley list_splice_init(&list, asd_ha->seq.pend_q.prev);
12122908d778SJames Bottomley asd_write_reg_dword(asd_ha, SCBPRO, (u32)asd_ha->seq.scbpro);
12132908d778SJames Bottomley spin_unlock_irqrestore(&asd_ha->seq.pend_q_lock, flags);
12142908d778SJames Bottomley
12152908d778SJames Bottomley return 0;
12162908d778SJames Bottomley }
12172908d778SJames Bottomley
12182908d778SJames Bottomley /**
12192908d778SJames Bottomley * asd_post_escb_list -- post a list of 1 or more empty scb
12202908d778SJames Bottomley * @asd_ha: pointer to a host adapter structure
12212908d778SJames Bottomley * @ascb: pointer to the first empty SCB in the list
12222908d778SJames Bottomley * @num: number of aSCBs in the list (to be posted)
12232908d778SJames Bottomley *
12242908d778SJames Bottomley * This is essentially the same as asd_post_ascb_list, but we do not
12252908d778SJames Bottomley * increment pending, add those to the pending list or get indexes.
12262908d778SJames Bottomley * See asd_init_escbs() and asd_init_post_escbs().
12272908d778SJames Bottomley *
12282908d778SJames Bottomley * Since sending a list of ascbs is a superset of sending a single
12292908d778SJames Bottomley * ascb, this function exists to generalize this. More specifically,
12302908d778SJames Bottomley * when sending a list of those, we want to do only a _single_
12312908d778SJames Bottomley * memcpy() at swap head, as opposed to for each ascb sent (in the
12322908d778SJames Bottomley * case of sending them one by one). That is, we want to minimize the
12332908d778SJames Bottomley * ratio of memcpy() operations to the number of ascbs sent. The same
12342908d778SJames Bottomley * logic applies to asd_post_ascb_list().
12352908d778SJames Bottomley */
asd_post_escb_list(struct asd_ha_struct * asd_ha,struct asd_ascb * ascb,int num)12362908d778SJames Bottomley int asd_post_escb_list(struct asd_ha_struct *asd_ha, struct asd_ascb *ascb,
12372908d778SJames Bottomley int num)
12382908d778SJames Bottomley {
12392908d778SJames Bottomley unsigned long flags;
12402908d778SJames Bottomley
12412908d778SJames Bottomley spin_lock_irqsave(&asd_ha->seq.pend_q_lock, flags);
12422908d778SJames Bottomley asd_swap_head_scb(asd_ha, ascb);
12432908d778SJames Bottomley asd_ha->seq.scbpro += num;
12442908d778SJames Bottomley asd_write_reg_dword(asd_ha, SCBPRO, (u32)asd_ha->seq.scbpro);
12452908d778SJames Bottomley spin_unlock_irqrestore(&asd_ha->seq.pend_q_lock, flags);
12462908d778SJames Bottomley
12472908d778SJames Bottomley return 0;
12482908d778SJames Bottomley }
12492908d778SJames Bottomley
12502908d778SJames Bottomley /* ---------- LED ---------- */
12512908d778SJames Bottomley
12522908d778SJames Bottomley /**
12532908d778SJames Bottomley * asd_turn_led -- turn on/off an LED
12542908d778SJames Bottomley * @asd_ha: pointer to host adapter structure
12552908d778SJames Bottomley * @phy_id: the PHY id whose LED we want to manupulate
12562908d778SJames Bottomley * @op: 1 to turn on, 0 to turn off
12572908d778SJames Bottomley */
asd_turn_led(struct asd_ha_struct * asd_ha,int phy_id,int op)12582908d778SJames Bottomley void asd_turn_led(struct asd_ha_struct *asd_ha, int phy_id, int op)
12592908d778SJames Bottomley {
12602908d778SJames Bottomley if (phy_id < ASD_MAX_PHYS) {
12612908d778SJames Bottomley u32 v = asd_read_reg_dword(asd_ha, LmCONTROL(phy_id));
12622908d778SJames Bottomley if (op)
12632908d778SJames Bottomley v |= LEDPOL;
12642908d778SJames Bottomley else
12652908d778SJames Bottomley v &= ~LEDPOL;
12662908d778SJames Bottomley asd_write_reg_dword(asd_ha, LmCONTROL(phy_id), v);
12672908d778SJames Bottomley }
12682908d778SJames Bottomley }
12692908d778SJames Bottomley
12702908d778SJames Bottomley /**
12712908d778SJames Bottomley * asd_control_led -- enable/disable an LED on the board
12722908d778SJames Bottomley * @asd_ha: pointer to host adapter structure
12732908d778SJames Bottomley * @phy_id: integer, the phy id
12742908d778SJames Bottomley * @op: integer, 1 to enable, 0 to disable the LED
12752908d778SJames Bottomley *
12762908d778SJames Bottomley * First we output enable the LED, then we set the source
12772908d778SJames Bottomley * to be an external module.
12782908d778SJames Bottomley */
asd_control_led(struct asd_ha_struct * asd_ha,int phy_id,int op)12792908d778SJames Bottomley void asd_control_led(struct asd_ha_struct *asd_ha, int phy_id, int op)
12802908d778SJames Bottomley {
12812908d778SJames Bottomley if (phy_id < ASD_MAX_PHYS) {
12822908d778SJames Bottomley u32 v;
12832908d778SJames Bottomley
12842908d778SJames Bottomley v = asd_read_reg_dword(asd_ha, GPIOOER);
12852908d778SJames Bottomley if (op)
12862908d778SJames Bottomley v |= (1 << phy_id);
12872908d778SJames Bottomley else
12882908d778SJames Bottomley v &= ~(1 << phy_id);
12892908d778SJames Bottomley asd_write_reg_dword(asd_ha, GPIOOER, v);
12902908d778SJames Bottomley
12912908d778SJames Bottomley v = asd_read_reg_dword(asd_ha, GPIOCNFGR);
12922908d778SJames Bottomley if (op)
12932908d778SJames Bottomley v |= (1 << phy_id);
12942908d778SJames Bottomley else
12952908d778SJames Bottomley v &= ~(1 << phy_id);
12962908d778SJames Bottomley asd_write_reg_dword(asd_ha, GPIOCNFGR, v);
12972908d778SJames Bottomley }
12982908d778SJames Bottomley }
12992908d778SJames Bottomley
13002908d778SJames Bottomley /* ---------- PHY enable ---------- */
13012908d778SJames Bottomley
asd_enable_phy(struct asd_ha_struct * asd_ha,int phy_id)13022908d778SJames Bottomley static int asd_enable_phy(struct asd_ha_struct *asd_ha, int phy_id)
13032908d778SJames Bottomley {
13042908d778SJames Bottomley struct asd_phy *phy = &asd_ha->phys[phy_id];
13052908d778SJames Bottomley
13062908d778SJames Bottomley asd_write_reg_byte(asd_ha, LmSEQ_OOB_REG(phy_id, INT_ENABLE_2), 0);
13072908d778SJames Bottomley asd_write_reg_byte(asd_ha, LmSEQ_OOB_REG(phy_id, HOT_PLUG_DELAY),
13082908d778SJames Bottomley HOTPLUG_DELAY_TIMEOUT);
13092908d778SJames Bottomley
13102908d778SJames Bottomley /* Get defaults from manuf. sector */
13112908d778SJames Bottomley /* XXX we need defaults for those in case MS is broken. */
13122908d778SJames Bottomley asd_write_reg_byte(asd_ha, LmSEQ_OOB_REG(phy_id, PHY_CONTROL_0),
13132908d778SJames Bottomley phy->phy_desc->phy_control_0);
13142908d778SJames Bottomley asd_write_reg_byte(asd_ha, LmSEQ_OOB_REG(phy_id, PHY_CONTROL_1),
13152908d778SJames Bottomley phy->phy_desc->phy_control_1);
13162908d778SJames Bottomley asd_write_reg_byte(asd_ha, LmSEQ_OOB_REG(phy_id, PHY_CONTROL_2),
13172908d778SJames Bottomley phy->phy_desc->phy_control_2);
13182908d778SJames Bottomley asd_write_reg_byte(asd_ha, LmSEQ_OOB_REG(phy_id, PHY_CONTROL_3),
13192908d778SJames Bottomley phy->phy_desc->phy_control_3);
13202908d778SJames Bottomley
13212908d778SJames Bottomley asd_write_reg_dword(asd_ha, LmSEQ_TEN_MS_COMINIT_TIMEOUT(phy_id),
13222908d778SJames Bottomley ASD_COMINIT_TIMEOUT);
13232908d778SJames Bottomley
13242908d778SJames Bottomley asd_write_reg_addr(asd_ha, LmSEQ_TX_ID_ADDR_FRAME(phy_id),
13252908d778SJames Bottomley phy->id_frm_tok->dma_handle);
13262908d778SJames Bottomley
13272908d778SJames Bottomley asd_control_led(asd_ha, phy_id, 1);
13282908d778SJames Bottomley
13292908d778SJames Bottomley return 0;
13302908d778SJames Bottomley }
13312908d778SJames Bottomley
asd_enable_phys(struct asd_ha_struct * asd_ha,const u8 phy_mask)13322908d778SJames Bottomley int asd_enable_phys(struct asd_ha_struct *asd_ha, const u8 phy_mask)
13332908d778SJames Bottomley {
13342908d778SJames Bottomley u8 phy_m;
13352908d778SJames Bottomley u8 i;
13362908d778SJames Bottomley int num = 0, k;
13372908d778SJames Bottomley struct asd_ascb *ascb;
13382908d778SJames Bottomley struct asd_ascb *ascb_list;
13392908d778SJames Bottomley
13402908d778SJames Bottomley if (!phy_mask) {
1341cadbd4a5SHarvey Harrison asd_printk("%s called with phy_mask of 0!?\n", __func__);
13422908d778SJames Bottomley return 0;
13432908d778SJames Bottomley }
13442908d778SJames Bottomley
13452908d778SJames Bottomley for_each_phy(phy_mask, phy_m, i) {
13462908d778SJames Bottomley num++;
13472908d778SJames Bottomley asd_enable_phy(asd_ha, i);
13482908d778SJames Bottomley }
13492908d778SJames Bottomley
13502908d778SJames Bottomley k = num;
13512908d778SJames Bottomley ascb_list = asd_ascb_alloc_list(asd_ha, &k, GFP_KERNEL);
13522908d778SJames Bottomley if (!ascb_list) {
13532908d778SJames Bottomley asd_printk("no memory for control phy ascb list\n");
13542908d778SJames Bottomley return -ENOMEM;
13552908d778SJames Bottomley }
13562908d778SJames Bottomley num -= k;
13572908d778SJames Bottomley
13582908d778SJames Bottomley ascb = ascb_list;
13592908d778SJames Bottomley for_each_phy(phy_mask, phy_m, i) {
13602908d778SJames Bottomley asd_build_control_phy(ascb, i, ENABLE_PHY);
13612908d778SJames Bottomley ascb = list_entry(ascb->list.next, struct asd_ascb, list);
13622908d778SJames Bottomley }
13632908d778SJames Bottomley ASD_DPRINTK("posting %d control phy scbs\n", num);
13642908d778SJames Bottomley k = asd_post_ascb_list(asd_ha, ascb_list, num);
13652908d778SJames Bottomley if (k)
13662908d778SJames Bottomley asd_ascb_free_list(ascb_list);
13672908d778SJames Bottomley
13682908d778SJames Bottomley return k;
13692908d778SJames Bottomley }
1370