⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 aic94xx_seq.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 4 页
字号:
/* * 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 + -