// 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 <[email protected]> * * Parts of this code adapted from David Chaw's adp94xx_seq.c. */ #include <linux/delay.h> #include <linux/gfp.h> #include <linux/pci.h> #include <linux/module.h> #include <linux/firmware.h> #include "aic94xx_reg.h" #include "aic94xx_hwi.h" #include "aic94xx_seq.h" #include "aic94xx_dump.h" /* 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 … #define PAUSE_TRIES … static const struct firmware *sequencer_fw; static u16 cseq_vecs[CSEQ_NUM_VECS], lseq_vecs[LSEQ_NUM_VECS], mode2_task, cseq_idle_loop, lseq_idle_loop; static const u8 *cseq_code, *lseq_code; static u32 cseq_code_size, lseq_code_size; static u16 first_scb_site_no = …; static u16 last_scb_site_no; /* ---------- Pause/Unpause CSEQ/LSEQ ---------- */ /** * asd_pause_cseq - pause the central sequencer * @asd_ha: pointer to host adapter structure * * Return 0 on success, negative on failure. */ static int asd_pause_cseq(struct asd_ha_struct *asd_ha) { … } /** * asd_unpause_cseq - unpause the central sequencer. * @asd_ha: pointer to host adapter structure. * * Return 0 on success, negative on error. */ static int asd_unpause_cseq(struct asd_ha_struct *asd_ha) { … } /** * 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. */ static int asd_seq_pause_lseq(struct asd_ha_struct *asd_ha, int lseq) { … } /** * 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. */ static int asd_pause_lseq(struct asd_ha_struct *asd_ha, u8 lseq_mask) { … } /** * 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. */ static int asd_seq_unpause_lseq(struct asd_ha_struct *asd_ha, int lseq) { … } /* ---------- Downloading CSEQ/LSEQ microcode ---------- */ static int asd_verify_cseq(struct asd_ha_struct *asd_ha, const u8 *_prog, u32 size) { … } /** * 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. */ static int asd_verify_lseq(struct asd_ha_struct *asd_ha, const u8 *_prog, u32 size, int lseq) { … } /** * asd_verify_seq -- verify CSEQ/LSEQ microcode * @asd_ha: pointer to host adapter structure * @prog: pointer to microcode * @size: size of the microcode * @lseq_mask: if 0, verify CSEQ microcode, else mask of LSEQs of interest * * Return 0 if microcode is correct, negative on mismatch. */ static int asd_verify_seq(struct asd_ha_struct *asd_ha, const u8 *prog, u32 size, u8 lseq_mask) { … } #define ASD_DMA_MODE_DOWNLOAD #ifdef ASD_DMA_MODE_DOWNLOAD /* This is the size of the CSEQ Mapped instruction page */ #define MAX_DMA_OVLY_COUNT … static int asd_download_seq(struct asd_ha_struct *asd_ha, const u8 * const prog, u32 size, u8 lseq_mask) { … } #else /* ASD_DMA_MODE_DOWNLOAD */ static int asd_download_seq(struct asd_ha_struct *asd_ha, const u8 *_prog, u32 size, u8 lseq_mask) { int i; u32 reg = 0; const u32 *prog = (u32 *) _prog; if (size % 4) { asd_printk("sequencer program not multiple of 4\n"); return -1; } asd_pause_cseq(asd_ha); asd_pause_lseq(asd_ha, 0xFF); reg |= (lseq_mask ? (((u32)lseq_mask) << 8) : OVLYCSEQ); reg |= PIOCMODE; asd_write_reg_dword(asd_ha, OVLYDMACNT, size); asd_write_reg_dword(asd_ha, OVLYDMACTL, reg); ASD_DPRINTK("downloading %s sequencer%s in PIO mode...\n", lseq_mask ? "LSEQ" : "CSEQ", lseq_mask ? "s" : ""); for (i = 0; i < size; i += 4, prog++) asd_write_reg_dword(asd_ha, SPIODATA, *prog); reg = (reg & ~PIOCMODE) | OVLYHALTERR; asd_write_reg_dword(asd_ha, OVLYDMACTL, reg); return asd_verify_seq(asd_ha, _prog, size, lseq_mask); } #endif /* ASD_DMA_MODE_DOWNLOAD */ /** * asd_seq_download_seqs - download the sequencer microcode * @asd_ha: pointer to host adapter structure * * Download the central and link sequencer microcode. */ static int asd_seq_download_seqs(struct asd_ha_struct *asd_ha) { … } /* ---------- Initializing the chip, chip memory, etc. ---------- */ /** * asd_init_cseq_mip - initialize CSEQ mode independent pages 4-7 * @asd_ha: pointer to host adapter structure */ static void asd_init_cseq_mip(struct asd_ha_struct *asd_ha) { … } /** * asd_init_cseq_mdp - initialize CSEQ Mode dependent pages * @asd_ha: pointer to host adapter structure */ static void asd_init_cseq_mdp(struct asd_ha_struct *asd_ha) { … } /** * asd_init_cseq_scratch -- setup and init CSEQ * @asd_ha: pointer to host adapter structure * * Setup and initialize Central sequencers. Initialize the mode * independent and dependent scratch page to the default settings. */ static void asd_init_cseq_scratch(struct asd_ha_struct *asd_ha) { … } /** * asd_init_lseq_mip -- initialize LSEQ Mode independent pages 0-3 * @asd_ha: pointer to host adapter structure * @lseq: link sequencer */ static void asd_init_lseq_mip(struct asd_ha_struct *asd_ha, u8 lseq) { … } /** * asd_init_lseq_mdp -- initialize LSEQ mode dependent pages. * @asd_ha: pointer to host adapter structure * @lseq: link sequencer */ static void asd_init_lseq_mdp(struct asd_ha_struct *asd_ha, int lseq) { … } /** * asd_init_lseq_scratch -- setup and init link sequencers * @asd_ha: pointer to host adapter struct */ static void asd_init_lseq_scratch(struct asd_ha_struct *asd_ha) { … } /** * 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. */ static void asd_init_scb_sites(struct asd_ha_struct *asd_ha) { … } /** * asd_init_cseq_cio - initialize CSEQ CIO registers * @asd_ha: pointer to host adapter structure */ static void asd_init_cseq_cio(struct asd_ha_struct *asd_ha) { … } /** * asd_init_lseq_cio -- initialize LmSEQ CIO registers * @asd_ha: pointer to host adapter structure * @lseq: link sequencer */ static void asd_init_lseq_cio(struct asd_ha_struct *asd_ha, int lseq) { … } /** * asd_post_init_cseq -- clear CSEQ Mode n Int. status and Response mailbox * @asd_ha: pointer to host adapter struct */ static void asd_post_init_cseq(struct asd_ha_struct *asd_ha) { … } /** * 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. */ static void asd_init_ddb_0(struct asd_ha_struct *asd_ha) { … } static void asd_seq_init_ddb_sites(struct asd_ha_struct *asd_ha) { … } /** * asd_seq_setup_seqs -- setup and initialize central and link sequencers * @asd_ha: pointer to host adapter structure */ static void asd_seq_setup_seqs(struct asd_ha_struct *asd_ha) { … } /** * asd_seq_start_cseq -- start the central sequencer, CSEQ * @asd_ha: pointer to host adapter structure */ static int asd_seq_start_cseq(struct asd_ha_struct *asd_ha) { … } /** * asd_seq_start_lseq -- start a link sequencer * @asd_ha: pointer to host adapter structure * @lseq: the link sequencer of interest */ static int asd_seq_start_lseq(struct asd_ha_struct *asd_ha, int lseq) { … } int asd_release_firmware(void) { … } static int asd_request_firmware(struct asd_ha_struct *asd_ha) { … } int asd_init_seqs(struct asd_ha_struct *asd_ha) { … } int asd_start_seqs(struct asd_ha_struct *asd_ha) { … } /** * 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) { … } MODULE_FIRMWARE(…);