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

📄 sata_sx4.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 3 页
字号:
/* *  sata_sx4.c - Promise SATA * *  Maintained by:  Jeff Garzik <jgarzik@pobox.com> *  		    Please ALWAYS copy linux-ide@vger.kernel.org *		    on emails. * *  Copyright 2003-2004 Red Hat, Inc. * * *  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.* * *  Hardware documentation available under NDA. * *//*	Theory of operation	-------------------	The SX4 (PDC20621) chip features a single Host DMA (HDMA) copy	engine, DIMM memory, and four ATA engines (one per SATA port).	Data is copied to/from DIMM memory by the HDMA engine, before	handing off to one (or more) of the ATA engines.  The ATA	engines operate solely on DIMM memory.	The SX4 behaves like a PATA chip, with no SATA controls or	knowledge whatsoever, leading to the presumption that	PATA<->SATA bridges exist on SX4 boards, external to the	PDC20621 chip itself.	The chip is quite capable, supporting an XOR engine and linked	hardware commands (permits a string to transactions to be	submitted and waited-on as a single unit), and an optional	microprocessor.	The limiting factor is largely software.  This Linux driver was	written to multiplex the single HDMA engine to copy disk	transactions into a fixed DIMM memory space, from where an ATA	engine takes over.  As a result, each WRITE looks like this:		submit HDMA packet to hardware		hardware copies data from system memory to DIMM		hardware raises interrupt		submit ATA packet to hardware		hardware executes ATA WRITE command, w/ data in DIMM		hardware raises interrupt	and each READ looks like this:		submit ATA packet to hardware		hardware executes ATA READ command, w/ data in DIMM		hardware raises interrupt		submit HDMA packet to hardware		hardware copies data from DIMM to system memory		hardware raises interrupt	This is a very slow, lock-step way of doing things that can	certainly be improved by motivated kernel hackers. */#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 <scsi/scsi_cmnd.h>#include <linux/libata.h>#include "sata_promise.h"#define DRV_NAME	"sata_sx4"#define DRV_VERSION	"0.12"enum {	PDC_MMIO_BAR		= 3,	PDC_DIMM_BAR		= 4,	PDC_PRD_TBL		= 0x44,	/* Direct command DMA table addr */	PDC_PKT_SUBMIT		= 0x40, /* Command packet pointer addr */	PDC_HDMA_PKT_SUBMIT	= 0x100, /* Host DMA packet pointer addr */	PDC_INT_SEQMASK		= 0x40,	/* Mask of asserted SEQ INTs */	PDC_HDMA_CTLSTAT	= 0x12C, /* Host DMA control / status */	PDC_CTLSTAT		= 0x60,	/* IDEn control / status */	PDC_20621_SEQCTL	= 0x400,	PDC_20621_SEQMASK	= 0x480,	PDC_20621_GENERAL_CTL	= 0x484,	PDC_20621_PAGE_SIZE	= (32 * 1024),	/* chosen, not constant, values; we design our own DIMM mem map */	PDC_20621_DIMM_WINDOW	= 0x0C,	/* page# for 32K DIMM window */	PDC_20621_DIMM_BASE	= 0x00200000,	PDC_20621_DIMM_DATA	= (64 * 1024),	PDC_DIMM_DATA_STEP	= (256 * 1024),	PDC_DIMM_WINDOW_STEP	= (8 * 1024),	PDC_DIMM_HOST_PRD	= (6 * 1024),	PDC_DIMM_HOST_PKT	= (128 * 0),	PDC_DIMM_HPKT_PRD	= (128 * 1),	PDC_DIMM_ATA_PKT	= (128 * 2),	PDC_DIMM_APKT_PRD	= (128 * 3),	PDC_DIMM_HEADER_SZ	= PDC_DIMM_APKT_PRD + 128,	PDC_PAGE_WINDOW		= 0x40,	PDC_PAGE_DATA		= PDC_PAGE_WINDOW +				  (PDC_20621_DIMM_DATA / PDC_20621_PAGE_SIZE),	PDC_PAGE_SET		= PDC_DIMM_DATA_STEP / PDC_20621_PAGE_SIZE,	PDC_CHIP0_OFS		= 0xC0000, /* offset of chip #0 */	PDC_20621_ERR_MASK	= (1<<19) | (1<<20) | (1<<21) | (1<<22) |				  (1<<23),	board_20621		= 0,	/* FastTrak S150 SX4 */	PDC_MASK_INT		= (1 << 10), /* HDMA/ATA mask int */	PDC_RESET		= (1 << 11), /* HDMA/ATA reset */	PDC_DMA_ENABLE		= (1 << 7),  /* DMA start/stop */	PDC_MAX_HDMA		= 32,	PDC_HDMA_Q_MASK		= (PDC_MAX_HDMA - 1),	PDC_DIMM0_SPD_DEV_ADDRESS	= 0x50,	PDC_DIMM1_SPD_DEV_ADDRESS	= 0x51,	PDC_I2C_CONTROL			= 0x48,	PDC_I2C_ADDR_DATA		= 0x4C,	PDC_DIMM0_CONTROL		= 0x80,	PDC_DIMM1_CONTROL		= 0x84,	PDC_SDRAM_CONTROL		= 0x88,	PDC_I2C_WRITE			= 0,		/* master -> slave */	PDC_I2C_READ			= (1 << 6),	/* master <- slave */	PDC_I2C_START			= (1 << 7),	/* start I2C proto */	PDC_I2C_MASK_INT		= (1 << 5),	/* mask I2C interrupt */	PDC_I2C_COMPLETE		= (1 << 16),	/* I2C normal compl. */	PDC_I2C_NO_ACK			= (1 << 20),	/* slave no-ack addr */	PDC_DIMM_SPD_SUBADDRESS_START	= 0x00,	PDC_DIMM_SPD_SUBADDRESS_END	= 0x7F,	PDC_DIMM_SPD_ROW_NUM		= 3,	PDC_DIMM_SPD_COLUMN_NUM		= 4,	PDC_DIMM_SPD_MODULE_ROW		= 5,	PDC_DIMM_SPD_TYPE		= 11,	PDC_DIMM_SPD_FRESH_RATE		= 12,	PDC_DIMM_SPD_BANK_NUM		= 17,	PDC_DIMM_SPD_CAS_LATENCY	= 18,	PDC_DIMM_SPD_ATTRIBUTE		= 21,	PDC_DIMM_SPD_ROW_PRE_CHARGE	= 27,	PDC_DIMM_SPD_ROW_ACTIVE_DELAY	= 28,	PDC_DIMM_SPD_RAS_CAS_DELAY	= 29,	PDC_DIMM_SPD_ACTIVE_PRECHARGE	= 30,	PDC_DIMM_SPD_SYSTEM_FREQ	= 126,	PDC_CTL_STATUS			= 0x08,	PDC_DIMM_WINDOW_CTLR		= 0x0C,	PDC_TIME_CONTROL		= 0x3C,	PDC_TIME_PERIOD			= 0x40,	PDC_TIME_COUNTER		= 0x44,	PDC_GENERAL_CTLR		= 0x484,	PCI_PLL_INIT			= 0x8A531824,	PCI_X_TCOUNT			= 0xEE1E5CFF,	/* PDC_TIME_CONTROL bits */	PDC_TIMER_BUZZER		= (1 << 10),	PDC_TIMER_MODE_PERIODIC		= 0,		/* bits 9:8 == 00 */	PDC_TIMER_MODE_ONCE		= (1 << 8),	/* bits 9:8 == 01 */	PDC_TIMER_ENABLE		= (1 << 7),	PDC_TIMER_MASK_INT		= (1 << 5),	PDC_TIMER_SEQ_MASK		= 0x1f,		/* SEQ ID for timer */	PDC_TIMER_DEFAULT		= PDC_TIMER_MODE_ONCE |					  PDC_TIMER_ENABLE |					  PDC_TIMER_MASK_INT,};struct pdc_port_priv {	u8			dimm_buf[(ATA_PRD_SZ * ATA_MAX_PRD) + 512];	u8			*pkt;	dma_addr_t		pkt_dma;};struct pdc_host_priv {	unsigned int		doing_hdma;	unsigned int		hdma_prod;	unsigned int		hdma_cons;	struct {		struct ata_queued_cmd *qc;		unsigned int	seq;		unsigned long	pkt_ofs;	} hdma[32];};static int pdc_sata_init_one(struct pci_dev *pdev, const struct pci_device_id *ent);static void pdc_eng_timeout(struct ata_port *ap);static void pdc_20621_phy_reset(struct ata_port *ap);static int pdc_port_start(struct ata_port *ap);static void pdc20621_qc_prep(struct ata_queued_cmd *qc);static void pdc_tf_load_mmio(struct ata_port *ap, const struct ata_taskfile *tf);static void pdc_exec_command_mmio(struct ata_port *ap, const struct ata_taskfile *tf);static unsigned int pdc20621_dimm_init(struct ata_host *host);static int pdc20621_detect_dimm(struct ata_host *host);static unsigned int pdc20621_i2c_read(struct ata_host *host,				      u32 device, u32 subaddr, u32 *pdata);static int pdc20621_prog_dimm0(struct ata_host *host);static unsigned int pdc20621_prog_dimm_global(struct ata_host *host);#ifdef ATA_VERBOSE_DEBUGstatic void pdc20621_get_from_dimm(struct ata_host *host,				   void *psource, u32 offset, u32 size);#endifstatic void pdc20621_put_to_dimm(struct ata_host *host,				 void *psource, u32 offset, u32 size);static void pdc20621_irq_clear(struct ata_port *ap);static unsigned int pdc20621_qc_issue_prot(struct ata_queued_cmd *qc);static struct scsi_host_template pdc_sata_sht = {	.module			= THIS_MODULE,	.name			= DRV_NAME,	.ioctl			= ata_scsi_ioctl,	.queuecommand		= ata_scsi_queuecmd,	.can_queue		= ATA_DEF_QUEUE,	.this_id		= ATA_SHT_THIS_ID,	.sg_tablesize		= LIBATA_MAX_PRD,	.cmd_per_lun		= ATA_SHT_CMD_PER_LUN,	.emulated		= ATA_SHT_EMULATED,	.use_clustering		= ATA_SHT_USE_CLUSTERING,	.proc_name		= DRV_NAME,	.dma_boundary		= ATA_DMA_BOUNDARY,	.slave_configure	= ata_scsi_slave_config,	.slave_destroy		= ata_scsi_slave_destroy,	.bios_param		= ata_std_bios_param,};static const struct ata_port_operations pdc_20621_ops = {	.tf_load		= pdc_tf_load_mmio,	.tf_read		= ata_tf_read,	.check_status		= ata_check_status,	.exec_command		= pdc_exec_command_mmio,	.dev_select		= ata_std_dev_select,	.phy_reset		= pdc_20621_phy_reset,	.qc_prep		= pdc20621_qc_prep,	.qc_issue		= pdc20621_qc_issue_prot,	.data_xfer		= ata_data_xfer,	.eng_timeout		= pdc_eng_timeout,	.irq_clear		= pdc20621_irq_clear,	.irq_on			= ata_irq_on,	.port_start		= pdc_port_start,};static const struct ata_port_info pdc_port_info[] = {	/* board_20621 */	{		.flags		= ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY |				  ATA_FLAG_SRST | ATA_FLAG_MMIO |				  ATA_FLAG_NO_ATAPI | ATA_FLAG_PIO_POLLING,		.pio_mask	= 0x1f, /* pio0-4 */		.mwdma_mask	= 0x07, /* mwdma0-2 */		.udma_mask	= ATA_UDMA6,		.port_ops	= &pdc_20621_ops,	},};static const struct pci_device_id pdc_sata_pci_tbl[] = {	{ PCI_VDEVICE(PROMISE, 0x6622), board_20621 },	{ }	/* terminate list */};static struct pci_driver pdc_sata_pci_driver = {	.name			= DRV_NAME,	.id_table		= pdc_sata_pci_tbl,	.probe			= pdc_sata_init_one,	.remove			= ata_pci_remove_one,};static int pdc_port_start(struct ata_port *ap){	struct device *dev = ap->host->dev;	struct pdc_port_priv *pp;	int rc;	rc = ata_port_start(ap);	if (rc)		return rc;	pp = devm_kzalloc(dev, sizeof(*pp), GFP_KERNEL);	if (!pp)		return -ENOMEM;	pp->pkt = dmam_alloc_coherent(dev, 128, &pp->pkt_dma, GFP_KERNEL);	if (!pp->pkt)		return -ENOMEM;	ap->private_data = pp;	return 0;}static void pdc_20621_phy_reset(struct ata_port *ap){	VPRINTK("ENTER\n");	ap->cbl = ATA_CBL_SATA;	ata_port_probe(ap);	ata_bus_reset(ap);}static inline void pdc20621_ata_sg(struct ata_taskfile *tf, u8 *buf,				   unsigned int portno,					   unsigned int total_len){	u32 addr;	unsigned int dw = PDC_DIMM_APKT_PRD >> 2;	u32 *buf32 = (u32 *) buf;	/* output ATA packet S/G table */	addr = PDC_20621_DIMM_BASE + PDC_20621_DIMM_DATA +	       (PDC_DIMM_DATA_STEP * portno);	VPRINTK("ATA sg addr 0x%x, %d\n", addr, addr);	buf32[dw] = cpu_to_le32(addr);	buf32[dw + 1] = cpu_to_le32(total_len | ATA_PRD_EOT);	VPRINTK("ATA PSG @ %x == (0x%x, 0x%x)\n",		PDC_20621_DIMM_BASE +		       (PDC_DIMM_WINDOW_STEP * portno) +		       PDC_DIMM_APKT_PRD,		buf32[dw], buf32[dw + 1]);}static inline void pdc20621_host_sg(struct ata_taskfile *tf, u8 *buf,				    unsigned int portno,					    unsigned int total_len){	u32 addr;	unsigned int dw = PDC_DIMM_HPKT_PRD >> 2;	u32 *buf32 = (u32 *) buf;	/* output Host DMA packet S/G table */	addr = PDC_20621_DIMM_BASE + PDC_20621_DIMM_DATA +	       (PDC_DIMM_DATA_STEP * portno);	buf32[dw] = cpu_to_le32(addr);	buf32[dw + 1] = cpu_to_le32(total_len | ATA_PRD_EOT);	VPRINTK("HOST PSG @ %x == (0x%x, 0x%x)\n",		PDC_20621_DIMM_BASE +		       (PDC_DIMM_WINDOW_STEP * portno) +		       PDC_DIMM_HPKT_PRD,		buf32[dw], buf32[dw + 1]);}static inline unsigned int pdc20621_ata_pkt(struct ata_taskfile *tf,					    unsigned int devno, u8 *buf,					    unsigned int portno){	unsigned int i, dw;	u32 *buf32 = (u32 *) buf;	u8 dev_reg;	unsigned int dimm_sg = PDC_20621_DIMM_BASE +			       (PDC_DIMM_WINDOW_STEP * portno) +			       PDC_DIMM_APKT_PRD;	VPRINTK("ENTER, dimm_sg == 0x%x, %d\n", dimm_sg, dimm_sg);	i = PDC_DIMM_ATA_PKT;	/*	 * Set up ATA packet	 */	if ((tf->protocol == ATA_PROT_DMA) && (!(tf->flags & ATA_TFLAG_WRITE)))		buf[i++] = PDC_PKT_READ;	else if (tf->protocol == ATA_PROT_NODATA)		buf[i++] = PDC_PKT_NODATA;	else		buf[i++] = 0;	buf[i++] = 0;			/* reserved */	buf[i++] = portno + 1;		/* seq. id */	buf[i++] = 0xff;		/* delay seq. id */	/* dimm dma S/G, and next-pkt */	dw = i >> 2;	if (tf->protocol == ATA_PROT_NODATA)		buf32[dw] = 0;	else		buf32[dw] = cpu_to_le32(dimm_sg);	buf32[dw + 1] = 0;	i += 8;	if (devno == 0)		dev_reg = ATA_DEVICE_OBS;	else		dev_reg = ATA_DEVICE_OBS | ATA_DEV1;	/* select device */	buf[i++] = (1 << 5) | PDC_PKT_CLEAR_BSY | ATA_REG_DEVICE;	buf[i++] = dev_reg;	/* device control register */	buf[i++] = (1 << 5) | PDC_REG_DEVCTL;	buf[i++] = tf->ctl;	return i;}static inline void pdc20621_host_pkt(struct ata_taskfile *tf, u8 *buf,				     unsigned int portno){	unsigned int dw;	u32 tmp, *buf32 = (u32 *) buf;	unsigned int host_sg = PDC_20621_DIMM_BASE +			       (PDC_DIMM_WINDOW_STEP * portno) +			       PDC_DIMM_HOST_PRD;	unsigned int dimm_sg = PDC_20621_DIMM_BASE +			       (PDC_DIMM_WINDOW_STEP * portno) +			       PDC_DIMM_HPKT_PRD;	VPRINTK("ENTER, dimm_sg == 0x%x, %d\n", dimm_sg, dimm_sg);	VPRINTK("host_sg == 0x%x, %d\n", host_sg, host_sg);	dw = PDC_DIMM_HOST_PKT >> 2;	/*	 * Set up Host DMA packet	 */	if ((tf->protocol == ATA_PROT_DMA) && (!(tf->flags & ATA_TFLAG_WRITE)))		tmp = PDC_PKT_READ;	else		tmp = 0;	tmp |= ((portno + 1 + 4) << 16);	/* seq. id */	tmp |= (0xff << 24);			/* delay seq. id */	buf32[dw + 0] = cpu_to_le32(tmp);	buf32[dw + 1] = cpu_to_le32(host_sg);	buf32[dw + 2] = cpu_to_le32(dimm_sg);	buf32[dw + 3] = 0;	VPRINTK("HOST PKT @ %x == (0x%x 0x%x 0x%x 0x%x)\n",		PDC_20621_DIMM_BASE + (PDC_DIMM_WINDOW_STEP * portno) +			PDC_DIMM_HOST_PKT,		buf32[dw + 0],		buf32[dw + 1],		buf32[dw + 2],		buf32[dw + 3]);}static void pdc20621_dma_prep(struct ata_queued_cmd *qc){	struct scatterlist *sg;	struct ata_port *ap = qc->ap;	struct pdc_port_priv *pp = ap->private_data;	void __iomem *mmio = ap->host->iomap[PDC_MMIO_BAR];	void __iomem *dimm_mmio = ap->host->iomap[PDC_DIMM_BAR];	unsigned int portno = ap->port_no;	unsigned int i, idx, total_len = 0, sgt_len;	u32 *buf = (u32 *) &pp->dimm_buf[PDC_DIMM_HEADER_SZ];	WARN_ON(!(qc->flags & ATA_QCFLAG_DMAMAP));	VPRINTK("ata%u: ENTER\n", ap->print_id);	/* hard-code chip #0 */	mmio += PDC_CHIP0_OFS;	/*	 * Build S/G table	 */

⌨️ 快捷键说明

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