pdc_adma.c

来自「linux 内核源代码」· C语言 代码 · 共 747 行 · 第 1/2 页

C
747
字号
/* *  pdc_adma.c - Pacific Digital Corporation ADMA * *  Maintained by:  Mark Lord <mlord@pobox.com> * *  Copyright 2005 Mark Lord * *  This program 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; either version 2, or (at your option) *  any later version. * *  This program 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 this program; see the file COPYING.  If not, write to *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. * * *  libata documentation is available via 'make {ps|pdf}docs', *  as Documentation/DocBook/libata.* * * *  Supports ATA disks in single-packet ADMA mode. *  Uses PIO for everything else. * *  TODO:  Use ADMA transfers for ATAPI devices, when possible. *  This requires careful attention to a number of quirks of the chip. * */#include <linux/kernel.h>#include <linux/module.h>#include <linux/pci.h>#include <linux/init.h>#include <linux/blkdev.h>#include <linux/delay.h>#include <linux/interrupt.h>#include <linux/device.h>#include <scsi/scsi_host.h>#include <linux/libata.h>#define DRV_NAME	"pdc_adma"#define DRV_VERSION	"1.0"/* macro to calculate base address for ATA regs */#define ADMA_ATA_REGS(base, port_no)	((base) + ((port_no) * 0x40))/* macro to calculate base address for ADMA regs */#define ADMA_REGS(base, port_no)	((base) + 0x80 + ((port_no) * 0x20))/* macro to obtain addresses from ata_port */#define ADMA_PORT_REGS(ap) \	ADMA_REGS((ap)->host->iomap[ADMA_MMIO_BAR], ap->port_no)enum {	ADMA_MMIO_BAR		= 4,	ADMA_PORTS		= 2,	ADMA_CPB_BYTES		= 40,	ADMA_PRD_BYTES		= LIBATA_MAX_PRD * 16,	ADMA_PKT_BYTES		= ADMA_CPB_BYTES + ADMA_PRD_BYTES,	ADMA_DMA_BOUNDARY	= 0xffffffff,	/* global register offsets */	ADMA_MODE_LOCK		= 0x00c7,	/* per-channel register offsets */	ADMA_CONTROL		= 0x0000, /* ADMA control */	ADMA_STATUS		= 0x0002, /* ADMA status */	ADMA_CPB_COUNT		= 0x0004, /* CPB count */	ADMA_CPB_CURRENT	= 0x000c, /* current CPB address */	ADMA_CPB_NEXT		= 0x000c, /* next CPB address */	ADMA_CPB_LOOKUP		= 0x0010, /* CPB lookup table */	ADMA_FIFO_IN		= 0x0014, /* input FIFO threshold */	ADMA_FIFO_OUT		= 0x0016, /* output FIFO threshold */	/* ADMA_CONTROL register bits */	aNIEN			= (1 << 8), /* irq mask: 1==masked */	aGO			= (1 << 7), /* packet trigger ("Go!") */	aRSTADM			= (1 << 5), /* ADMA logic reset */	aPIOMD4			= 0x0003,   /* PIO mode 4 */	/* ADMA_STATUS register bits */	aPSD			= (1 << 6),	aUIRQ			= (1 << 4),	aPERR			= (1 << 0),	/* CPB bits */	cDONE			= (1 << 0),	cATERR			= (1 << 3),	cVLD			= (1 << 0),	cDAT			= (1 << 2),	cIEN			= (1 << 3),	/* PRD bits */	pORD			= (1 << 4),	pDIRO			= (1 << 5),	pEND			= (1 << 7),	/* ATA register flags */	rIGN			= (1 << 5),	rEND			= (1 << 7),	/* ATA register addresses */	ADMA_REGS_CONTROL	= 0x0e,	ADMA_REGS_SECTOR_COUNT	= 0x12,	ADMA_REGS_LBA_LOW	= 0x13,	ADMA_REGS_LBA_MID	= 0x14,	ADMA_REGS_LBA_HIGH	= 0x15,	ADMA_REGS_DEVICE	= 0x16,	ADMA_REGS_COMMAND	= 0x17,	/* PCI device IDs */	board_1841_idx		= 0,	/* ADMA 2-port controller */};typedef enum { adma_state_idle, adma_state_pkt, adma_state_mmio } adma_state_t;struct adma_port_priv {	u8			*pkt;	dma_addr_t		pkt_dma;	adma_state_t		state;};static int adma_ata_init_one(struct pci_dev *pdev,				const struct pci_device_id *ent);static int adma_port_start(struct ata_port *ap);static void adma_host_stop(struct ata_host *host);static void adma_port_stop(struct ata_port *ap);static void adma_qc_prep(struct ata_queued_cmd *qc);static unsigned int adma_qc_issue(struct ata_queued_cmd *qc);static int adma_check_atapi_dma(struct ata_queued_cmd *qc);static void adma_bmdma_stop(struct ata_queued_cmd *qc);static u8 adma_bmdma_status(struct ata_port *ap);static void adma_irq_clear(struct ata_port *ap);static void adma_freeze(struct ata_port *ap);static void adma_thaw(struct ata_port *ap);static void adma_error_handler(struct ata_port *ap);static struct scsi_host_template adma_ata_sht = {	.module			= THIS_MODULE,	.name			= DRV_NAME,	.ioctl			= ata_scsi_ioctl,	.queuecommand		= ata_scsi_queuecmd,	.slave_configure	= ata_scsi_slave_config,	.slave_destroy		= ata_scsi_slave_destroy,	.bios_param		= ata_std_bios_param,	.proc_name		= DRV_NAME,	.can_queue		= ATA_DEF_QUEUE,	.this_id		= ATA_SHT_THIS_ID,	.sg_tablesize		= LIBATA_MAX_PRD,	.dma_boundary		= ADMA_DMA_BOUNDARY,	.cmd_per_lun		= ATA_SHT_CMD_PER_LUN,	.use_clustering		= ENABLE_CLUSTERING,	.emulated		= ATA_SHT_EMULATED,};static const struct ata_port_operations adma_ata_ops = {	.tf_load		= ata_tf_load,	.tf_read		= ata_tf_read,	.exec_command		= ata_exec_command,	.check_status		= ata_check_status,	.dev_select		= ata_std_dev_select,	.check_atapi_dma	= adma_check_atapi_dma,	.data_xfer		= ata_data_xfer,	.qc_prep		= adma_qc_prep,	.qc_issue		= adma_qc_issue,	.freeze			= adma_freeze,	.thaw			= adma_thaw,	.error_handler		= adma_error_handler,	.irq_clear		= adma_irq_clear,	.irq_on			= ata_irq_on,	.port_start		= adma_port_start,	.port_stop		= adma_port_stop,	.host_stop		= adma_host_stop,	.bmdma_stop		= adma_bmdma_stop,	.bmdma_status		= adma_bmdma_status,};static struct ata_port_info adma_port_info[] = {	/* board_1841_idx */	{		.flags		= ATA_FLAG_SLAVE_POSS |				  ATA_FLAG_NO_LEGACY | ATA_FLAG_MMIO |				  ATA_FLAG_PIO_POLLING,		.pio_mask	= 0x10, /* pio4 */		.udma_mask	= ATA_UDMA4,		.port_ops	= &adma_ata_ops,	},};static const struct pci_device_id adma_ata_pci_tbl[] = {	{ PCI_VDEVICE(PDC, 0x1841), board_1841_idx },	{ }	/* terminate list */};static struct pci_driver adma_ata_pci_driver = {	.name			= DRV_NAME,	.id_table		= adma_ata_pci_tbl,	.probe			= adma_ata_init_one,	.remove			= ata_pci_remove_one,};static int adma_check_atapi_dma(struct ata_queued_cmd *qc){	return 1;	/* ATAPI DMA not yet supported */}static void adma_bmdma_stop(struct ata_queued_cmd *qc){	/* nothing */}static u8 adma_bmdma_status(struct ata_port *ap){	return 0;}static void adma_irq_clear(struct ata_port *ap){	/* nothing */}static void adma_reset_engine(struct ata_port *ap){	void __iomem *chan = ADMA_PORT_REGS(ap);	/* reset ADMA to idle state */	writew(aPIOMD4 | aNIEN | aRSTADM, chan + ADMA_CONTROL);	udelay(2);	writew(aPIOMD4, chan + ADMA_CONTROL);	udelay(2);}static void adma_reinit_engine(struct ata_port *ap){	struct adma_port_priv *pp = ap->private_data;	void __iomem *chan = ADMA_PORT_REGS(ap);	/* mask/clear ATA interrupts */	writeb(ATA_NIEN, ap->ioaddr.ctl_addr);	ata_check_status(ap);	/* reset the ADMA engine */	adma_reset_engine(ap);	/* set in-FIFO threshold to 0x100 */	writew(0x100, chan + ADMA_FIFO_IN);	/* set CPB pointer */	writel((u32)pp->pkt_dma, chan + ADMA_CPB_NEXT);	/* set out-FIFO threshold to 0x100 */	writew(0x100, chan + ADMA_FIFO_OUT);	/* set CPB count */	writew(1, chan + ADMA_CPB_COUNT);	/* read/discard ADMA status */	readb(chan + ADMA_STATUS);}static inline void adma_enter_reg_mode(struct ata_port *ap){	void __iomem *chan = ADMA_PORT_REGS(ap);	writew(aPIOMD4, chan + ADMA_CONTROL);	readb(chan + ADMA_STATUS);	/* flush */}static void adma_freeze(struct ata_port *ap){	void __iomem *chan = ADMA_PORT_REGS(ap);	/* mask/clear ATA interrupts */	writeb(ATA_NIEN, ap->ioaddr.ctl_addr);	ata_check_status(ap);	/* reset ADMA to idle state */	writew(aPIOMD4 | aNIEN | aRSTADM, chan + ADMA_CONTROL);	udelay(2);	writew(aPIOMD4 | aNIEN, chan + ADMA_CONTROL);	udelay(2);}static void adma_thaw(struct ata_port *ap){	adma_reinit_engine(ap);}static int adma_prereset(struct ata_link *link, unsigned long deadline){	struct ata_port *ap = link->ap;	struct adma_port_priv *pp = ap->private_data;	if (pp->state != adma_state_idle) /* healthy paranoia */		pp->state = adma_state_mmio;	adma_reinit_engine(ap);	return ata_std_prereset(link, deadline);}static void adma_error_handler(struct ata_port *ap){	ata_do_eh(ap, adma_prereset, ata_std_softreset, NULL,		  ata_std_postreset);}static int adma_fill_sg(struct ata_queued_cmd *qc){	struct scatterlist *sg;	struct ata_port *ap = qc->ap;	struct adma_port_priv *pp = ap->private_data;	u8  *buf = pp->pkt, *last_buf = NULL;	int i = (2 + buf[3]) * 8;	u8 pFLAGS = pORD | ((qc->tf.flags & ATA_TFLAG_WRITE) ? pDIRO : 0);	ata_for_each_sg(sg, qc) {		u32 addr;		u32 len;		addr = (u32)sg_dma_address(sg);		*(__le32 *)(buf + i) = cpu_to_le32(addr);		i += 4;		len = sg_dma_len(sg) >> 3;		*(__le32 *)(buf + i) = cpu_to_le32(len);		i += 4;		last_buf = &buf[i];		buf[i++] = pFLAGS;		buf[i++] = qc->dev->dma_mode & 0xf;		buf[i++] = 0;	/* pPKLW */		buf[i++] = 0;	/* reserved */		*(__le32 *)(buf + i) =			(pFLAGS & pEND) ? 0 : cpu_to_le32(pp->pkt_dma + i + 4);		i += 4;		VPRINTK("PRD[%u] = (0x%lX, 0x%X)\n", i/4,					(unsigned long)addr, len);	}	if (likely(last_buf))		*last_buf |= pEND;	return i;}static void adma_qc_prep(struct ata_queued_cmd *qc){	struct adma_port_priv *pp = qc->ap->private_data;	u8  *buf = pp->pkt;	u32 pkt_dma = (u32)pp->pkt_dma;	int i = 0;	VPRINTK("ENTER\n");	adma_enter_reg_mode(qc->ap);	if (qc->tf.protocol != ATA_PROT_DMA) {		ata_qc_prep(qc);		return;	}	buf[i++] = 0;	/* Response flags */	buf[i++] = 0;	/* reserved */	buf[i++] = cVLD | cDAT | cIEN;

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?