📄 aic94xx_seq.c
字号:
/* * 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. * * 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/delay.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 1#define PAUSE_TRIES 1000static 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 u8 *cseq_code, *lseq_code;static u32 cseq_code_size, lseq_code_size;static u16 first_scb_site_no = 0xFFFF;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. */int asd_pause_cseq(struct asd_ha_struct *asd_ha){ int count = PAUSE_TRIES; u32 arp2ctl; 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 pause CSEQ\n"); return -1;}/** * asd_unpause_cseq - unpause the central sequencer. * @asd_ha: pointer to host adapter structure. * * Return 0 on success, negative on error. */int 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. */static inline int 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_DPRINTK("couldn't pause LSEQ %d\n", lseq); return -1;}/** * 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. */int asd_pause_lseq(struct asd_ha_struct *asd_ha, u8 lseq_mask){ int lseq; int err = 0; for_each_sequencer(lseq_mask, lseq_mask, lseq) { err = asd_seq_pause_lseq(asd_ha, lseq); if (err) return err; } return err;}/** * 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 inline int 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); ASD_DPRINTK("couldn't unpause LSEQ %d\n", lseq); return 0;}/** * asd_unpause_lseq - unpause 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. */int asd_unpause_lseq(struct asd_ha_struct *asd_ha, u8 lseq_mask){ int lseq; int err = 0; for_each_sequencer(lseq_mask, lseq_mask, lseq) { err = asd_seq_unpause_lseq(asd_ha, lseq); if (err) return err; } return err;}/* ---------- Downloading CSEQ/LSEQ microcode ---------- */static int asd_verify_cseq(struct asd_ha_struct *asd_ha, const u8 *_prog, u32 size){ u32 addr = CSEQ_RAM_REG_BASE_ADR; const u32 *prog = (u32 *) _prog; u32 i; 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. */static int 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); if (le32_to_cpu(*prog) != val) { asd_printk("%s: LSEQ%d verify failed " "page:%d, offs:%d\n", pci_name(asd_ha->pcidev), lseq, page, i); return -1; } } } ASD_DPRINTK("LSEQ%d verified %d bytes, passed\n", lseq, (int)((u8 *)prog-_prog)); return 0;}/** * 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){ if (lseq_mask == 0) return asd_verify_cseq(asd_ha, prog, size); else { int lseq, err; for_each_sequencer(lseq_mask, lseq_mask, lseq) { err = asd_verify_lseq(asd_ha, prog, size, lseq); if (err) return err; } } return 0;}#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 ((1U << 14)-1)static int asd_download_seq(struct asd_ha_struct *asd_ha, const u8 * const prog, u32 size, u8 lseq_mask){ u32 comstaten; u32 reg; int page; const int pages = (size + MAX_DMA_OVLY_COUNT - 1) / MAX_DMA_OVLY_COUNT; struct asd_dma_tok *token; int err = 0; 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); /* save, disable and clear interrupts */ comstaten = asd_read_reg_dword(asd_ha, COMSTATEN); asd_write_reg_dword(asd_ha, COMSTATEN, 0); asd_write_reg_dword(asd_ha, COMSTAT, COMSTAT_MASK); asd_write_reg_dword(asd_ha, CHIMINTEN, RST_CHIMINTEN); asd_write_reg_dword(asd_ha, CHIMINT, CHIMINT_MASK); token = asd_alloc_coherent(asd_ha, MAX_DMA_OVLY_COUNT, GFP_KERNEL); if (!token) { asd_printk("out of memory for dma SEQ download\n"); err = -ENOMEM; goto out; } ASD_DPRINTK("dma-ing %d bytes\n", size); for (page = 0; page < pages; page++) { int i; u32 left = min(size-page*MAX_DMA_OVLY_COUNT, (u32)MAX_DMA_OVLY_COUNT); memcpy(token->vaddr, prog + page*MAX_DMA_OVLY_COUNT, left); asd_write_reg_addr(asd_ha, OVLYDMAADR, token->dma_handle); asd_write_reg_dword(asd_ha, OVLYDMACNT, left); reg = !page ? RESETOVLYDMA : 0; reg |= (STARTOVLYDMA | OVLYHALTERR); reg |= (lseq_mask ? (((u32)lseq_mask) << 8) : OVLYCSEQ); /* Start DMA. */ asd_write_reg_dword(asd_ha, OVLYDMACTL, reg); for (i = PAUSE_TRIES*100; i > 0; i--) { u32 dmadone = asd_read_reg_dword(asd_ha, OVLYDMACTL); if (!(dmadone & OVLYDMAACT)) break;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -