// SPDX-License-Identifier: GPL-2.0-only /* * Aic94xx SAS/SATA driver sequencer interface. * * Copyright (C) 2005 Adaptec, Inc. All rights reserved. * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com> * * Parts of this code adapted from David Chaw's adp94xx_seq.c.
*/
/* It takes no more than 0.05 us for an instruction * to complete. So waiting for 1 us should be more than * plenty.
*/ #define PAUSE_DELAY 1 #define PAUSE_TRIES 1000
/** * asd_unpause_cseq - unpause the central sequencer. * @asd_ha: pointer to host adapter structure. * * Return 0 on success, negative on error.
*/ staticint asd_unpause_cseq(struct asd_ha_struct *asd_ha)
{
u32 arp2ctl; int count = PAUSE_TRIES;
arp2ctl = asd_read_reg_dword(asd_ha, CARP2CTL); if (!(arp2ctl & PAUSED)) return 0;
asd_write_reg_dword(asd_ha, CARP2CTL, arp2ctl & ~EPAUSE); do {
arp2ctl = asd_read_reg_dword(asd_ha, CARP2CTL); if (!(arp2ctl & PAUSED)) return 0;
udelay(PAUSE_DELAY);
} while (--count > 0);
ASD_DPRINTK("couldn't unpause the CSEQ\n"); return -1;
}
/** * asd_seq_pause_lseq - pause a link sequencer * @asd_ha: pointer to a host adapter structure * @lseq: link sequencer of interest * * Return 0 on success, negative on error.
*/ staticint asd_seq_pause_lseq(struct asd_ha_struct *asd_ha, int lseq)
{
u32 arp2ctl; int count = PAUSE_TRIES;
arp2ctl = asd_read_reg_dword(asd_ha, LmARP2CTL(lseq)); if (arp2ctl & PAUSED) return 0;
asd_write_reg_dword(asd_ha, LmARP2CTL(lseq), arp2ctl | EPAUSE); do {
arp2ctl = asd_read_reg_dword(asd_ha, LmARP2CTL(lseq)); if (arp2ctl & PAUSED) return 0;
udelay(PAUSE_DELAY);
} while (--count > 0);
/** * asd_pause_lseq - pause the link sequencer(s) * @asd_ha: pointer to host adapter structure * @lseq_mask: mask of link sequencers of interest * * Return 0 on success, negative on failure.
*/ staticint asd_pause_lseq(struct asd_ha_struct *asd_ha, u8 lseq_mask)
{ int lseq; int err = 0;
/** * asd_seq_unpause_lseq - unpause a link sequencer * @asd_ha: pointer to host adapter structure * @lseq: link sequencer of interest * * Return 0 on success, negative on error.
*/ staticint asd_seq_unpause_lseq(struct asd_ha_struct *asd_ha, int lseq)
{
u32 arp2ctl; int count = PAUSE_TRIES;
arp2ctl = asd_read_reg_dword(asd_ha, LmARP2CTL(lseq)); if (!(arp2ctl & PAUSED)) return 0;
asd_write_reg_dword(asd_ha, LmARP2CTL(lseq), arp2ctl & ~EPAUSE); do {
arp2ctl = asd_read_reg_dword(asd_ha, LmARP2CTL(lseq)); if (!(arp2ctl & PAUSED)) return 0;
udelay(PAUSE_DELAY);
} while (--count > 0);
for (i = 0; i < size; i += 4, prog++, addr += 4) {
u32 val = asd_read_reg_dword(asd_ha, addr);
if (le32_to_cpu(*prog) != val) {
asd_printk("%s: cseq verify failed at %u " "read:0x%x, wanted:0x%x\n",
pci_name(asd_ha->pcidev),
i, val, le32_to_cpu(*prog)); return -1;
}
}
ASD_DPRINTK("verified %d bytes, passed\n", size); return 0;
}
/** * asd_verify_lseq - verify the microcode of a link sequencer * @asd_ha: pointer to host adapter structure * @_prog: pointer to the microcode * @size: size of the microcode in bytes * @lseq: link sequencer of interest * * The link sequencer code is accessed in 4 KB pages, which are selected * by setting LmRAMPAGE (bits 8 and 9) of the LmBISTCTL1 register. * The 10 KB LSEQm instruction code is mapped, page at a time, at * LmSEQRAM address.
*/ staticint asd_verify_lseq(struct asd_ha_struct *asd_ha, const u8 *_prog,
u32 size, int lseq)
{ #define LSEQ_CODEPAGE_SIZE 4096 int pages = (size + LSEQ_CODEPAGE_SIZE - 1) / LSEQ_CODEPAGE_SIZE;
u32 page; const u32 *prog = (u32 *) _prog;
for (page = 0; page < pages; page++) {
u32 i;
asd_write_reg_dword(asd_ha, LmBISTCTL1(lseq),
page << LmRAMPAGE_LSHIFT); for (i = 0; size > 0 && i < LSEQ_CODEPAGE_SIZE;
i += 4, prog++, size-=4) {
u32 val = asd_read_reg_dword(asd_ha, LmSEQRAM(lseq)+i);
/** * asd_seq_download_seqs - download the sequencer microcode * @asd_ha: pointer to host adapter structure * * Download the central and link sequencer microcode.
*/ staticint asd_seq_download_seqs(struct asd_ha_struct *asd_ha)
{ int err;
if (!asd_ha->hw_prof.enabled_phys) {
asd_printk("%s: no enabled phys!\n", pci_name(asd_ha->pcidev)); return -ENODEV;
}
/* Download the Link Sequencers code. All of the Link Sequencers * microcode can be downloaded at the same time.
*/
ASD_DPRINTK("downloading LSEQs...\n");
err = asd_download_seq(asd_ha, lseq_code, lseq_code_size,
asd_ha->hw_prof.enabled_phys); if (err) { /* Try it one at a time */
u8 lseq;
u8 lseq_mask = asd_ha->hw_prof.enabled_phys;
/* CSEQ Mode dependent, mode 8, page 2 setup. */ /* Tell the sequencer the bus address of the first SCB. */
asd_write_reg_addr(asd_ha, CSEQ_HQ_NEW_POINTER,
asd_ha->seq.next_scb.dma_handle);
ASD_DPRINTK("First SCB dma_handle: 0x%llx\n",
(unsignedlonglong)asd_ha->seq.next_scb.dma_handle);
/* Tell the sequencer the first Done List entry address. */
asd_write_reg_addr(asd_ha, CSEQ_HQ_DONE_BASE,
asd_ha->seq.actual_dl->dma_handle);
/* Initialize the Q_DONE_POINTER with the least significant
* 4 bytes of the first Done List address. */
asd_write_reg_dword(asd_ha, CSEQ_HQ_DONE_POINTER,
ASD_BUSADDR_LO(asd_ha->seq.actual_dl->dma_handle));
/* LSEQ Mode dependent, mode 4/5, page 0 setup. */
asd_write_reg_byte(asd_ha, LmSEQ_SAVED_OOB_STATUS(lseq), 0);
asd_write_reg_byte(asd_ha, LmSEQ_SAVED_OOB_MODE(lseq), 0);
asd_write_reg_word(asd_ha, LmSEQ_Q_LINK_HEAD(lseq), 0xFFFF);
asd_write_reg_byte(asd_ha, LmSEQ_LINK_RST_ERR(lseq), 0);
asd_write_reg_byte(asd_ha, LmSEQ_SAVED_OOB_SIGNALS(lseq), 0);
asd_write_reg_byte(asd_ha, LmSEQ_SAS_RESET_MODE(lseq), 0);
asd_write_reg_byte(asd_ha, LmSEQ_LINK_RESET_RETRY_COUNT(lseq), 0);
asd_write_reg_byte(asd_ha, LmSEQ_NUM_LINK_RESET_RETRIES(lseq), 0);
asd_write_reg_word(asd_ha, LmSEQ_OOB_INT_ENABLES(lseq), 0); /* * Set the desired interval between transmissions of the NOTIFY * (ENABLE SPINUP) primitive. Must be initialized to val - 1.
*/
asd_write_reg_word(asd_ha, LmSEQ_NOTIFY_TIMER_TIMEOUT(lseq),
ASD_NOTIFY_TIMEOUT - 1); /* No delay for the first NOTIFY to be sent to the attached target. */
asd_write_reg_word(asd_ha, LmSEQ_NOTIFY_TIMER_DOWN_COUNT(lseq),
ASD_NOTIFY_DOWN_COUNT);
asd_write_reg_word(asd_ha, LmSEQ_NOTIFY_TIMER_INITIAL_COUNT(lseq),
ASD_NOTIFY_DOWN_COUNT);
/* LSEQ Mode dependent, mode 0 and 1, page 1 setup. */ for (i = 0; i < 2; i++) { int j; /* Start from Page 1 of Mode 0 and 1. */
moffs = LSEQ_PAGE_SIZE + i*LSEQ_MODE_SCRATCH_SIZE; /* All the fields of page 1 can be initialized to 0. */ for (j = 0; j < LSEQ_PAGE_SIZE; j += 4)
asd_write_reg_dword(asd_ha, LmSCRATCH(lseq)+moffs+j,0);
}
/** * asd_init_scb_sites -- initialize sequencer SCB sites (memory). * @asd_ha: pointer to host adapter structure * * This should be done before initializing common CSEQ and LSEQ * scratch since those areas depend on some computed values here, * last_scb_site_no, etc.
*/ staticvoid asd_init_scb_sites(struct asd_ha_struct *asd_ha)
{
u16 site_no;
u16 max_scbs = 0;
/* Initialize all fields in the SCB site to 0. */ for (i = 0; i < ASD_SCB_SIZE; i += 4)
asd_scbsite_write_dword(asd_ha, site_no, i, 0);
/* Initialize SCB Site Opcode field to invalid. */
asd_scbsite_write_byte(asd_ha, site_no,
offsetof(struct scb_header, opcode),
0xFF);
/* Initialize SCB Site Flags field to mean a response * frame has been received. This means inadvertent
* frames received to be dropped. */
asd_scbsite_write_byte(asd_ha, site_no, 0x49, 0x01);
/* Workaround needed by SEQ to fix a SATA issue is to exclude
* certain SCB sites from the free list. */ if (!SCB_SITE_VALID(site_no)) continue;
if (last_scb_site_no == 0)
last_scb_site_no = site_no;
/* For every SCB site, we need to initialize the * following fields: Q_NEXT, SCB_OPCODE, SCB_FLAGS,
* and SG Element Flag. */
/* Q_NEXT field of the last SCB is invalidated. */
asd_scbsite_write_word(asd_ha, site_no, 0, first_scb_site_no);
/* Initialize CSEQ Mode 11 Interrupt Vectors. * The addresses are 16 bit wide and in dword units. * The values of their macros are in byte units.
* Thus we have to divide by 4. */
asd_write_reg_word(asd_ha, CM11INTVEC0, cseq_vecs[0]);
asd_write_reg_word(asd_ha, CM11INTVEC1, cseq_vecs[1]);
asd_write_reg_word(asd_ha, CM11INTVEC2, cseq_vecs[2]);
/* Enable ARP2HALTC (ARP2 Halted from Halt Code Write). */
asd_write_reg_byte(asd_ha, CARP2INTEN, EN_ARP2HALTC);
/* Initialize CSEQ Mode[0-8] Dependent registers. */ /* Initialize Scratch Page to 0. */ for (i = 0; i < 9; i++)
asd_write_reg_byte(asd_ha, CMnSCRATCHPAGE(i), 0);
/* Reset the ARP2 Program Count. */
asd_write_reg_word(asd_ha, CPRGMCNT, cseq_idle_loop);
for (i = 0; i < 8; i++) { /* Initialize Mode n Link m Interrupt Enable. */
asd_write_reg_dword(asd_ha, CMnINTEN(i), EN_CMnRSPMBXF); /* Initialize Mode n Request Mailbox. */
asd_write_reg_dword(asd_ha, CMnREQMBX(i), 0);
}
}
/** * asd_init_lseq_cio -- initialize LmSEQ CIO registers * @asd_ha: pointer to host adapter structure * @lseq: link sequencer
*/ staticvoid asd_init_lseq_cio(struct asd_ha_struct *asd_ha, int lseq)
{
u8 *sas_addr; int i;
/* Enable ARP2HALTC (ARP2 Halted from Halt Code Write). */
asd_write_reg_dword(asd_ha, LmARP2INTEN(lseq), EN_ARP2HALTC);
/* Set the Phy SAS for the LmSEQ WWN. */
sas_addr = asd_ha->phys[lseq].phy_desc->sas_addr; for (i = 0; i < SAS_ADDR_SIZE; i++)
asd_write_reg_byte(asd_ha, LmWWN(lseq) + i, sas_addr[i]);
/* Set the Transmit Size to 1024 bytes, 0 = 256 Dwords. */
asd_write_reg_byte(asd_ha, LmMnXMTSIZE(lseq, 1), 0);
/* Set the Bus Inactivity Time Limit Timer. */
asd_write_reg_word(asd_ha, LmBITL_TIMER(lseq), 9);
/* Enable SATA Port Multiplier. */
asd_write_reg_byte(asd_ha, LmMnSATAFS(lseq, 1), 0x80);
/* Initialize Interrupt Vector[0-10] address in Mode 3.
* See the comment on CSEQ_INT_* */
asd_write_reg_word(asd_ha, LmM3INTVEC0(lseq), lseq_vecs[0]);
asd_write_reg_word(asd_ha, LmM3INTVEC1(lseq), lseq_vecs[1]);
asd_write_reg_word(asd_ha, LmM3INTVEC2(lseq), lseq_vecs[2]);
asd_write_reg_word(asd_ha, LmM3INTVEC3(lseq), lseq_vecs[3]);
asd_write_reg_word(asd_ha, LmM3INTVEC4(lseq), lseq_vecs[4]);
asd_write_reg_word(asd_ha, LmM3INTVEC5(lseq), lseq_vecs[5]);
asd_write_reg_word(asd_ha, LmM3INTVEC6(lseq), lseq_vecs[6]);
asd_write_reg_word(asd_ha, LmM3INTVEC7(lseq), lseq_vecs[7]);
asd_write_reg_word(asd_ha, LmM3INTVEC8(lseq), lseq_vecs[8]);
asd_write_reg_word(asd_ha, LmM3INTVEC9(lseq), lseq_vecs[9]);
asd_write_reg_word(asd_ha, LmM3INTVEC10(lseq), lseq_vecs[10]); /* * Program the Link LED control, applicable only for * Chip Rev. B or later.
*/
asd_write_reg_dword(asd_ha, LmCONTROL(lseq),
(LEDTIMER | LEDMODE_TXRX | LEDTIMERS_100ms));
/* Set the Align Rate for SAS and STP mode. */
asd_write_reg_byte(asd_ha, LmM1SASALIGN(lseq), SAS_ALIGN_DEFAULT);
asd_write_reg_byte(asd_ha, LmM1STPALIGN(lseq), STP_ALIGN_DEFAULT);
}
/** * asd_post_init_cseq -- clear CSEQ Mode n Int. status and Response mailbox * @asd_ha: pointer to host adapter struct
*/ staticvoid asd_post_init_cseq(struct asd_ha_struct *asd_ha)
{ int i;
for (i = 0; i < 8; i++)
asd_write_reg_dword(asd_ha, CMnINT(i), 0xFFFFFFFF); for (i = 0; i < 8; i++)
asd_read_reg_dword(asd_ha, CMnRSPMBX(i)); /* Reset the external interrupt arbiter. */
asd_write_reg_byte(asd_ha, CARP2INTCTL, RSTINTCTL);
}
/** * asd_init_ddb_0 -- initialize DDB 0 * @asd_ha: pointer to host adapter structure * * Initialize DDB site 0 which is used internally by the sequencer.
*/ staticvoid asd_init_ddb_0(struct asd_ha_struct *asd_ha)
{ int i;
/* Zero out the DDB explicitly */ for (i = 0; i < sizeof(struct asd_ddb_seq_shared); i+=4)
asd_ddbsite_write_dword(asd_ha, 0, i, 0);
for (ddb_site = 0 ; ddb_site < ASD_MAX_DDBS; ddb_site++) for (i = 0; i < sizeof(struct asd_ddb_ssp_smp_target_port); i+= 4)
asd_ddbsite_write_dword(asd_ha, ddb_site, i, 0);
}
/** * asd_seq_setup_seqs -- setup and initialize central and link sequencers * @asd_ha: pointer to host adapter structure
*/ staticvoid asd_seq_setup_seqs(struct asd_ha_struct *asd_ha)
{ int lseq;
u8 lseq_mask;
/** * asd_seq_start_cseq -- start the central sequencer, CSEQ * @asd_ha: pointer to host adapter structure
*/ staticint asd_seq_start_cseq(struct asd_ha_struct *asd_ha)
{ /* Reset the ARP2 instruction to location zero. */
asd_write_reg_word(asd_ha, CPRGMCNT, cseq_idle_loop);
/* Unpause the CSEQ */ return asd_unpause_cseq(asd_ha);
}
/** * asd_seq_start_lseq -- start a link sequencer * @asd_ha: pointer to host adapter structure * @lseq: the link sequencer of interest
*/ staticint asd_seq_start_lseq(struct asd_ha_struct *asd_ha, int lseq)
{ /* Reset the ARP2 instruction to location zero. */
asd_write_reg_word(asd_ha, LmPRGMCNT(lseq), lseq_idle_loop);
/* Unpause the LmSEQ */ return asd_seq_unpause_lseq(asd_ha, lseq);
}
int asd_release_firmware(void)
{
release_firmware(sequencer_fw); return 0;
}
asd_printk("Found sequencer Firmware version %d.%d (%s)\n",
header.major, header.minor, hdr_ptr->version);
if (header.major != SAS_RAZOR_SEQUENCER_FW_MAJOR) {
asd_printk("Firmware Major Version Mismatch;" "driver requires version %d.X",
SAS_RAZOR_SEQUENCER_FW_MAJOR); return -EINVAL;
}
/** * asd_update_port_links -- update port_map_by_links and phy_is_up * @asd_ha: pointer to host adapter structure * @phy: pointer to the phy which has been added to a port * * 1) When a link reset has completed and we got BYTES DMAED with a * valid frame we call this function for that phy, to indicate that * the phy is up, i.e. we update the phy_is_up in DDB 0. The * sequencer checks phy_is_up when pending SCBs are to be sent, and * when an open address frame has been received. * * 2) When we know of ports, we call this function to update the map * of phys participaing in that port, i.e. we update the * port_map_by_links in DDB 0. When a HARD_RESET primitive has been * received, the sequencer disables all phys in that port. * port_map_by_links is also used as the conn_mask byte in the * initiator/target port DDB.
*/ void asd_update_port_links(struct asd_ha_struct *asd_ha, struct asd_phy *phy)
{ const u8 phy_mask = (u8) phy->asd_port->phy_mask;
u8 phy_is_up;
u8 mask; int i, err; unsignedlong flags;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.