📄 aic94xx_hwi.c
字号:
/* * Aic94xx SAS/SATA driver hardware interface. * * Copyright (C) 2005 Adaptec, Inc. All rights reserved. * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com> * * This file is licensed under GPLv2. * * This file is part of the aic94xx driver. * * The aic94xx driver is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; version 2 of the * License. * * The aic94xx driver is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with the aic94xx driver; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */#include <linux/pci.h>#include <linux/delay.h>#include <linux/module.h>#include "aic94xx.h"#include "aic94xx_reg.h"#include "aic94xx_hwi.h"#include "aic94xx_seq.h"#include "aic94xx_dump.h"u32 MBAR0_SWB_SIZE;/* ---------- Initialization ---------- */static void asd_get_user_sas_addr(struct asd_ha_struct *asd_ha){ extern char sas_addr_str[]; /* If the user has specified a WWN it overrides other settings */ if (sas_addr_str[0] != '\0') asd_destringify_sas_addr(asd_ha->hw_prof.sas_addr, sas_addr_str); else if (asd_ha->hw_prof.sas_addr[0] != 0) asd_stringify_sas_addr(sas_addr_str, asd_ha->hw_prof.sas_addr);}static void asd_propagate_sas_addr(struct asd_ha_struct *asd_ha){ int i; for (i = 0; i < ASD_MAX_PHYS; i++) { if (asd_ha->hw_prof.phy_desc[i].sas_addr[0] == 0) continue; /* Set a phy's address only if it has none. */ ASD_DPRINTK("setting phy%d addr to %llx\n", i, SAS_ADDR(asd_ha->hw_prof.sas_addr)); memcpy(asd_ha->hw_prof.phy_desc[i].sas_addr, asd_ha->hw_prof.sas_addr, SAS_ADDR_SIZE); }}/* ---------- PHY initialization ---------- */static void asd_init_phy_identify(struct asd_phy *phy){ phy->identify_frame = phy->id_frm_tok->vaddr; memset(phy->identify_frame, 0, sizeof(*phy->identify_frame)); phy->identify_frame->dev_type = SAS_END_DEV; if (phy->sas_phy.role & PHY_ROLE_INITIATOR) phy->identify_frame->initiator_bits = phy->sas_phy.iproto; if (phy->sas_phy.role & PHY_ROLE_TARGET) phy->identify_frame->target_bits = phy->sas_phy.tproto; memcpy(phy->identify_frame->sas_addr, phy->phy_desc->sas_addr, SAS_ADDR_SIZE); phy->identify_frame->phy_id = phy->sas_phy.id;}static int asd_init_phy(struct asd_phy *phy){ struct asd_ha_struct *asd_ha = phy->sas_phy.ha->lldd_ha; struct asd_sas_phy *sas_phy = &phy->sas_phy; sas_phy->enabled = 1; sas_phy->class = SAS; sas_phy->iproto = SAS_PROTO_ALL; sas_phy->tproto = 0; sas_phy->type = PHY_TYPE_PHYSICAL; sas_phy->role = PHY_ROLE_INITIATOR; sas_phy->oob_mode = OOB_NOT_CONNECTED; sas_phy->linkrate = SAS_LINK_RATE_UNKNOWN; phy->id_frm_tok = asd_alloc_coherent(asd_ha, sizeof(*phy->identify_frame), GFP_KERNEL); if (!phy->id_frm_tok) { asd_printk("no mem for IDENTIFY for phy%d\n", sas_phy->id); return -ENOMEM; } else asd_init_phy_identify(phy); memset(phy->frame_rcvd, 0, sizeof(phy->frame_rcvd)); return 0;}static void asd_init_ports(struct asd_ha_struct *asd_ha){ int i; spin_lock_init(&asd_ha->asd_ports_lock); for (i = 0; i < ASD_MAX_PHYS; i++) { struct asd_port *asd_port = &asd_ha->asd_ports[i]; memset(asd_port->sas_addr, 0, SAS_ADDR_SIZE); memset(asd_port->attached_sas_addr, 0, SAS_ADDR_SIZE); asd_port->phy_mask = 0; asd_port->num_phys = 0; }}static int asd_init_phys(struct asd_ha_struct *asd_ha){ u8 i; u8 phy_mask = asd_ha->hw_prof.enabled_phys; for (i = 0; i < ASD_MAX_PHYS; i++) { struct asd_phy *phy = &asd_ha->phys[i]; phy->phy_desc = &asd_ha->hw_prof.phy_desc[i]; phy->asd_port = NULL; phy->sas_phy.enabled = 0; phy->sas_phy.id = i; phy->sas_phy.sas_addr = &phy->phy_desc->sas_addr[0]; phy->sas_phy.frame_rcvd = &phy->frame_rcvd[0]; phy->sas_phy.ha = &asd_ha->sas_ha; phy->sas_phy.lldd_phy = phy; } /* Now enable and initialize only the enabled phys. */ for_each_phy(phy_mask, phy_mask, i) { int err = asd_init_phy(&asd_ha->phys[i]); if (err) return err; } return 0;}/* ---------- Sliding windows ---------- */static int asd_init_sw(struct asd_ha_struct *asd_ha){ struct pci_dev *pcidev = asd_ha->pcidev; int err; u32 v; /* Unlock MBARs */ err = pci_read_config_dword(pcidev, PCI_CONF_MBAR_KEY, &v); if (err) { asd_printk("couldn't access conf. space of %s\n", pci_name(pcidev)); goto Err; } if (v) err = pci_write_config_dword(pcidev, PCI_CONF_MBAR_KEY, v); if (err) { asd_printk("couldn't write to MBAR_KEY of %s\n", pci_name(pcidev)); goto Err; } /* Set sliding windows A, B and C to point to proper internal * memory regions. */ pci_write_config_dword(pcidev, PCI_CONF_MBAR0_SWA, REG_BASE_ADDR); pci_write_config_dword(pcidev, PCI_CONF_MBAR0_SWB, REG_BASE_ADDR_CSEQCIO); pci_write_config_dword(pcidev, PCI_CONF_MBAR0_SWC, REG_BASE_ADDR_EXSI); asd_ha->io_handle[0].swa_base = REG_BASE_ADDR; asd_ha->io_handle[0].swb_base = REG_BASE_ADDR_CSEQCIO; asd_ha->io_handle[0].swc_base = REG_BASE_ADDR_EXSI; MBAR0_SWB_SIZE = asd_ha->io_handle[0].len - 0x80; if (!asd_ha->iospace) { /* MBAR1 will point to OCM (On Chip Memory) */ pci_write_config_dword(pcidev, PCI_CONF_MBAR1, OCM_BASE_ADDR); asd_ha->io_handle[1].swa_base = OCM_BASE_ADDR; } spin_lock_init(&asd_ha->iolock);Err: return err;}/* ---------- SCB initialization ---------- *//** * asd_init_scbs - manually allocate the first SCB. * @asd_ha: pointer to host adapter structure * * This allocates the very first SCB which would be sent to the * sequencer for execution. Its bus address is written to * CSEQ_Q_NEW_POINTER, mode page 2, mode 8. Since the bus address of * the _next_ scb to be DMA-ed to the host adapter is read from the last * SCB DMA-ed to the host adapter, we have to always stay one step * ahead of the sequencer and keep one SCB already allocated. */static int asd_init_scbs(struct asd_ha_struct *asd_ha){ struct asd_seq_data *seq = &asd_ha->seq; int bitmap_bytes; /* allocate the index array and bitmap */ asd_ha->seq.tc_index_bitmap_bits = asd_ha->hw_prof.max_scbs; asd_ha->seq.tc_index_array = kzalloc(asd_ha->seq.tc_index_bitmap_bits* sizeof(void *), GFP_KERNEL); if (!asd_ha->seq.tc_index_array) return -ENOMEM; bitmap_bytes = (asd_ha->seq.tc_index_bitmap_bits+7)/8; bitmap_bytes = BITS_TO_LONGS(bitmap_bytes*8)*sizeof(unsigned long); asd_ha->seq.tc_index_bitmap = kzalloc(bitmap_bytes, GFP_KERNEL); if (!asd_ha->seq.tc_index_bitmap) return -ENOMEM; spin_lock_init(&seq->tc_index_lock); seq->next_scb.size = sizeof(struct scb); seq->next_scb.vaddr = dma_pool_alloc(asd_ha->scb_pool, GFP_KERNEL, &seq->next_scb.dma_handle); if (!seq->next_scb.vaddr) { kfree(asd_ha->seq.tc_index_bitmap); kfree(asd_ha->seq.tc_index_array); asd_ha->seq.tc_index_bitmap = NULL; asd_ha->seq.tc_index_array = NULL; return -ENOMEM; } seq->pending = 0; spin_lock_init(&seq->pend_q_lock); INIT_LIST_HEAD(&seq->pend_q); return 0;}static inline void asd_get_max_scb_ddb(struct asd_ha_struct *asd_ha){ asd_ha->hw_prof.max_scbs = asd_get_cmdctx_size(asd_ha)/ASD_SCB_SIZE; asd_ha->hw_prof.max_ddbs = asd_get_devctx_size(asd_ha)/ASD_DDB_SIZE; ASD_DPRINTK("max_scbs:%d, max_ddbs:%d\n", asd_ha->hw_prof.max_scbs, asd_ha->hw_prof.max_ddbs);}/* ---------- Done List initialization ---------- */static void asd_dl_tasklet_handler(unsigned long);static int asd_init_dl(struct asd_ha_struct *asd_ha){ asd_ha->seq.actual_dl = asd_alloc_coherent(asd_ha, ASD_DL_SIZE * sizeof(struct done_list_struct), GFP_KERNEL); if (!asd_ha->seq.actual_dl) return -ENOMEM; asd_ha->seq.dl = asd_ha->seq.actual_dl->vaddr; asd_ha->seq.dl_toggle = ASD_DEF_DL_TOGGLE; asd_ha->seq.dl_next = 0; tasklet_init(&asd_ha->seq.dl_tasklet, asd_dl_tasklet_handler, (unsigned long) asd_ha); return 0;}/* ---------- EDB and ESCB init ---------- */static int asd_alloc_edbs(struct asd_ha_struct *asd_ha, gfp_t gfp_flags){ struct asd_seq_data *seq = &asd_ha->seq; int i; seq->edb_arr = kmalloc(seq->num_edbs*sizeof(*seq->edb_arr), gfp_flags); if (!seq->edb_arr) return -ENOMEM; for (i = 0; i < seq->num_edbs; i++) { seq->edb_arr[i] = asd_alloc_coherent(asd_ha, ASD_EDB_SIZE, gfp_flags); if (!seq->edb_arr[i]) goto Err_unroll; memset(seq->edb_arr[i]->vaddr, 0, ASD_EDB_SIZE); } ASD_DPRINTK("num_edbs:%d\n", seq->num_edbs); return 0;Err_unroll: for (i-- ; i >= 0; i--) asd_free_coherent(asd_ha, seq->edb_arr[i]); kfree(seq->edb_arr); seq->edb_arr = NULL; return -ENOMEM;}static int asd_alloc_escbs(struct asd_ha_struct *asd_ha, gfp_t gfp_flags){ struct asd_seq_data *seq = &asd_ha->seq; struct asd_ascb *escb; int i, escbs; seq->escb_arr = kmalloc(seq->num_escbs*sizeof(*seq->escb_arr), gfp_flags); if (!seq->escb_arr) return -ENOMEM; escbs = seq->num_escbs; escb = asd_ascb_alloc_list(asd_ha, &escbs, gfp_flags); if (!escb) { asd_printk("couldn't allocate list of escbs\n"); goto Err; } seq->num_escbs -= escbs; /* subtract what was not allocated */ ASD_DPRINTK("num_escbs:%d\n", seq->num_escbs); for (i = 0; i < seq->num_escbs; i++, escb = list_entry(escb->list.next, struct asd_ascb, list)) { seq->escb_arr[i] = escb; escb->scb->header.opcode = EMPTY_SCB; } return 0;Err: kfree(seq->escb_arr); seq->escb_arr = NULL; return -ENOMEM;}static void asd_assign_edbs2escbs(struct asd_ha_struct *asd_ha){ struct asd_seq_data *seq = &asd_ha->seq; int i, k, z = 0; for (i = 0; i < seq->num_escbs; i++) { struct asd_ascb *ascb = seq->escb_arr[i]; struct empty_scb *escb = &ascb->scb->escb; ascb->edb_index = z; escb->num_valid = ASD_EDBS_PER_SCB; for (k = 0; k < ASD_EDBS_PER_SCB; k++) { struct sg_el *eb = &escb->eb[k]; struct asd_dma_tok *edb = seq->edb_arr[z++]; memset(eb, 0, sizeof(*eb)); eb->bus_addr = cpu_to_le64(((u64) edb->dma_handle)); eb->size = cpu_to_le32(((u32) edb->size)); } }}/** * asd_init_escbs -- allocate and initialize empty scbs * @asd_ha: pointer to host adapter structure * * An empty SCB has sg_elements of ASD_EDBS_PER_SCB (7) buffers. * They transport sense data, etc. */static int asd_init_escbs(struct asd_ha_struct *asd_ha){ struct asd_seq_data *seq = &asd_ha->seq; int err = 0; /* Allocate two empty data buffers (edb) per sequencer. */ int edbs = 2*(1+asd_ha->hw_prof.num_phys); seq->num_escbs = (edbs+ASD_EDBS_PER_SCB-1)/ASD_EDBS_PER_SCB; seq->num_edbs = seq->num_escbs * ASD_EDBS_PER_SCB; err = asd_alloc_edbs(asd_ha, GFP_KERNEL); if (err) { asd_printk("couldn't allocate edbs\n"); return err; } err = asd_alloc_escbs(asd_ha, GFP_KERNEL); if (err) { asd_printk("couldn't allocate escbs\n"); return err; } asd_assign_edbs2escbs(asd_ha); /* In order to insure that normal SCBs do not overfill sequencer * memory and leave no space for escbs (halting condition), * we increment pending here by the number of escbs. However, * escbs are never pending. */ seq->pending = seq->num_escbs; seq->can_queue = 1 + (asd_ha->hw_prof.max_scbs - seq->pending)/2; return 0;}/* ---------- HW initialization ---------- *//** * asd_chip_hardrst -- hard reset the chip * @asd_ha: pointer to host adapter structure * * This takes 16 cycles and is synchronous to CFCLK, which runs * at 200 MHz, so this should take at most 80 nanoseconds. */int asd_chip_hardrst(struct asd_ha_struct *asd_ha){ int i; int count = 100; u32 reg; for (i = 0 ; i < 4 ; i++) { asd_write_reg_dword(asd_ha, COMBIST, HARDRST); } do { udelay(1); reg = asd_read_reg_dword(asd_ha, CHIMINT); if (reg & HARDRSTDET) { asd_write_reg_dword(asd_ha, CHIMINT, HARDRSTDET|PORRSTDET); return 0; } } while (--count > 0); return -ENODEV;}/** * asd_init_chip -- initialize the chip * @asd_ha: pointer to host adapter structure * * Hard resets the chip, disables HA interrupts, downloads the sequnecer * microcode and starts the sequencers. The caller has to explicitly * enable HA interrupts with asd_enable_ints(asd_ha). */static int asd_init_chip(struct asd_ha_struct *asd_ha){ int err; err = asd_chip_hardrst(asd_ha); if (err) { asd_printk("couldn't hard reset %s\n", pci_name(asd_ha->pcidev));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -