libata-core.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 2,786 行 · 第 1/5 页

C
2,786
字号
/*   libata-core.c - helper library for ATA   Copyright 2003-2004 Red Hat, Inc.  All rights reserved.   Copyright 2003-2004 Jeff Garzik   The contents of this file are subject to the Open   Software License version 1.1 that can be found at   http://www.opensource.org/licenses/osl-1.1.txt and is included herein   by reference.   Alternatively, the contents of this file may be used under the terms   of the GNU General Public License version 2 (the "GPL") as distributed   in the kernel source COPYING file, in which case the provisions of   the GPL are applicable instead of the above.  If you wish to allow   the use of your version of this file only under the terms of the   GPL and not to allow others to use your version of this file under   the OSL, indicate your decision by deleting the provisions above and   replace them with the notice and other provisions required by the GPL.   If you do not delete the provisions above, a recipient may use your   version of this file under either the OSL or the GPL. */#include <linux/config.h>#include <linux/kernel.h>#include <linux/module.h>#include <linux/pci.h>#include <linux/init.h>#include <linux/list.h>#include <linux/highmem.h>#include <linux/spinlock.h>#include <linux/blkdev.h>#include <linux/delay.h>#include <linux/timer.h>#include <linux/interrupt.h>#include <linux/completion.h>#include <linux/suspend.h>#include <linux/workqueue.h>#include <scsi/scsi.h>#include "scsi.h"#include <scsi/scsi_host.h>#include <linux/libata.h>#include <asm/io.h>#include <asm/semaphore.h>#include <asm/byteorder.h>#include "libata.h"static unsigned int ata_busy_sleep (struct ata_port *ap,				    unsigned long tmout_pat,			    	    unsigned long tmout);static void ata_set_mode(struct ata_port *ap);static void ata_dev_set_xfermode(struct ata_port *ap, struct ata_device *dev);static unsigned int ata_get_mode_mask(struct ata_port *ap, int shift);static int fgb(u32 bitmap);static int ata_choose_xfer_mode(struct ata_port *ap,				u8 *xfer_mode_out,				unsigned int *xfer_shift_out);static int ata_qc_complete_noop(struct ata_queued_cmd *qc, u8 drv_stat);static unsigned int ata_unique_id = 1;static struct workqueue_struct *ata_wq;MODULE_AUTHOR("Jeff Garzik");MODULE_DESCRIPTION("Library module for ATA devices");MODULE_LICENSE("GPL");/** *	ata_tf_load - send taskfile registers to host controller *	@ap: Port to which output is sent *	@tf: ATA taskfile register set * *	Outputs ATA taskfile to standard ATA host controller. * *	LOCKING: *	Inherited from caller. */static void ata_tf_load_pio(struct ata_port *ap, struct ata_taskfile *tf){	struct ata_ioports *ioaddr = &ap->ioaddr;	unsigned int is_addr = tf->flags & ATA_TFLAG_ISADDR;	if (tf->ctl != ap->last_ctl) {		outb(tf->ctl, ioaddr->ctl_addr);		ap->last_ctl = tf->ctl;		ata_wait_idle(ap);	}	if (is_addr && (tf->flags & ATA_TFLAG_LBA48)) {		outb(tf->hob_feature, ioaddr->feature_addr);		outb(tf->hob_nsect, ioaddr->nsect_addr);		outb(tf->hob_lbal, ioaddr->lbal_addr);		outb(tf->hob_lbam, ioaddr->lbam_addr);		outb(tf->hob_lbah, ioaddr->lbah_addr);		VPRINTK("hob: feat 0x%X nsect 0x%X, lba 0x%X 0x%X 0x%X\n",			tf->hob_feature,			tf->hob_nsect,			tf->hob_lbal,			tf->hob_lbam,			tf->hob_lbah);	}	if (is_addr) {		outb(tf->feature, ioaddr->feature_addr);		outb(tf->nsect, ioaddr->nsect_addr);		outb(tf->lbal, ioaddr->lbal_addr);		outb(tf->lbam, ioaddr->lbam_addr);		outb(tf->lbah, ioaddr->lbah_addr);		VPRINTK("feat 0x%X nsect 0x%X lba 0x%X 0x%X 0x%X\n",			tf->feature,			tf->nsect,			tf->lbal,			tf->lbam,			tf->lbah);	}	if (tf->flags & ATA_TFLAG_DEVICE) {		outb(tf->device, ioaddr->device_addr);		VPRINTK("device 0x%X\n", tf->device);	}	ata_wait_idle(ap);}/** *	ata_tf_load_mmio - send taskfile registers to host controller *	@ap: Port to which output is sent *	@tf: ATA taskfile register set * *	Outputs ATA taskfile to standard ATA host controller using MMIO. * *	LOCKING: *	Inherited from caller. */static void ata_tf_load_mmio(struct ata_port *ap, struct ata_taskfile *tf){	struct ata_ioports *ioaddr = &ap->ioaddr;	unsigned int is_addr = tf->flags & ATA_TFLAG_ISADDR;	if (tf->ctl != ap->last_ctl) {		writeb(tf->ctl, (void __iomem *) ap->ioaddr.ctl_addr);		ap->last_ctl = tf->ctl;		ata_wait_idle(ap);	}	if (is_addr && (tf->flags & ATA_TFLAG_LBA48)) {		writeb(tf->hob_feature, (void __iomem *) ioaddr->feature_addr);		writeb(tf->hob_nsect, (void __iomem *) ioaddr->nsect_addr);		writeb(tf->hob_lbal, (void __iomem *) ioaddr->lbal_addr);		writeb(tf->hob_lbam, (void __iomem *) ioaddr->lbam_addr);		writeb(tf->hob_lbah, (void __iomem *) ioaddr->lbah_addr);		VPRINTK("hob: feat 0x%X nsect 0x%X, lba 0x%X 0x%X 0x%X\n",			tf->hob_feature,			tf->hob_nsect,			tf->hob_lbal,			tf->hob_lbam,			tf->hob_lbah);	}	if (is_addr) {		writeb(tf->feature, (void __iomem *) ioaddr->feature_addr);		writeb(tf->nsect, (void __iomem *) ioaddr->nsect_addr);		writeb(tf->lbal, (void __iomem *) ioaddr->lbal_addr);		writeb(tf->lbam, (void __iomem *) ioaddr->lbam_addr);		writeb(tf->lbah, (void __iomem *) ioaddr->lbah_addr);		VPRINTK("feat 0x%X nsect 0x%X lba 0x%X 0x%X 0x%X\n",			tf->feature,			tf->nsect,			tf->lbal,			tf->lbam,			tf->lbah);	}	if (tf->flags & ATA_TFLAG_DEVICE) {		writeb(tf->device, (void __iomem *) ioaddr->device_addr);		VPRINTK("device 0x%X\n", tf->device);	}	ata_wait_idle(ap);}void ata_tf_load(struct ata_port *ap, struct ata_taskfile *tf){	if (ap->flags & ATA_FLAG_MMIO)		ata_tf_load_mmio(ap, tf);	else		ata_tf_load_pio(ap, tf);}/** *	ata_exec_command - issue ATA command to host controller *	@ap: port to which command is being issued *	@tf: ATA taskfile register set * *	Issues PIO/MMIO write to ATA command register, with proper *	synchronization with interrupt handler / other threads. * *	LOCKING: *	spin_lock_irqsave(host_set lock) */static void ata_exec_command_pio(struct ata_port *ap, struct ata_taskfile *tf){	DPRINTK("ata%u: cmd 0x%X\n", ap->id, tf->command);       	outb(tf->command, ap->ioaddr.command_addr);	ata_pause(ap);}/** *	ata_exec_command_mmio - issue ATA command to host controller *	@ap: port to which command is being issued *	@tf: ATA taskfile register set * *	Issues MMIO write to ATA command register, with proper *	synchronization with interrupt handler / other threads. * *	LOCKING: *	spin_lock_irqsave(host_set lock) */static void ata_exec_command_mmio(struct ata_port *ap, struct ata_taskfile *tf){	DPRINTK("ata%u: cmd 0x%X\n", ap->id, tf->command);       	writeb(tf->command, (void __iomem *) ap->ioaddr.command_addr);	ata_pause(ap);}void ata_exec_command(struct ata_port *ap, struct ata_taskfile *tf){	if (ap->flags & ATA_FLAG_MMIO)		ata_exec_command_mmio(ap, tf);	else		ata_exec_command_pio(ap, tf);}/** *	ata_exec - issue ATA command to host controller *	@ap: port to which command is being issued *	@tf: ATA taskfile register set * *	Issues PIO/MMIO write to ATA command register, with proper *	synchronization with interrupt handler / other threads. * *	LOCKING: *	Obtains host_set lock. */static inline void ata_exec(struct ata_port *ap, struct ata_taskfile *tf){	unsigned long flags;	DPRINTK("ata%u: cmd 0x%X\n", ap->id, tf->command);	spin_lock_irqsave(&ap->host_set->lock, flags);	ap->ops->exec_command(ap, tf);	spin_unlock_irqrestore(&ap->host_set->lock, flags);}/** *	ata_tf_to_host - issue ATA taskfile to host controller *	@ap: port to which command is being issued *	@tf: ATA taskfile register set * *	Issues ATA taskfile register set to ATA host controller, *	with proper synchronization with interrupt handler and *	other threads. * *	LOCKING: *	Obtains host_set lock. */static void ata_tf_to_host(struct ata_port *ap, struct ata_taskfile *tf){	ap->ops->tf_load(ap, tf);	ata_exec(ap, tf);}/** *	ata_tf_to_host_nolock - issue ATA taskfile to host controller *	@ap: port to which command is being issued *	@tf: ATA taskfile register set * *	Issues ATA taskfile register set to ATA host controller, *	with proper synchronization with interrupt handler and *	other threads. * *	LOCKING: *	spin_lock_irqsave(host_set lock) */void ata_tf_to_host_nolock(struct ata_port *ap, struct ata_taskfile *tf){	ap->ops->tf_load(ap, tf);	ap->ops->exec_command(ap, tf);}/** *	ata_tf_read - input device's ATA taskfile shadow registers *	@ap: Port from which input is read *	@tf: ATA taskfile register set for storing input * *	Reads ATA taskfile registers for currently-selected device *	into @tf. * *	LOCKING: *	Inherited from caller. */static void ata_tf_read_pio(struct ata_port *ap, struct ata_taskfile *tf){	struct ata_ioports *ioaddr = &ap->ioaddr;	tf->nsect = inb(ioaddr->nsect_addr);	tf->lbal = inb(ioaddr->lbal_addr);	tf->lbam = inb(ioaddr->lbam_addr);	tf->lbah = inb(ioaddr->lbah_addr);	tf->device = inb(ioaddr->device_addr);	if (tf->flags & ATA_TFLAG_LBA48) {		outb(tf->ctl | ATA_HOB, ioaddr->ctl_addr);		tf->hob_feature = inb(ioaddr->error_addr);		tf->hob_nsect = inb(ioaddr->nsect_addr);		tf->hob_lbal = inb(ioaddr->lbal_addr);		tf->hob_lbam = inb(ioaddr->lbam_addr);		tf->hob_lbah = inb(ioaddr->lbah_addr);	}}/** *	ata_tf_read_mmio - input device's ATA taskfile shadow registers *	@ap: Port from which input is read *	@tf: ATA taskfile register set for storing input * *	Reads ATA taskfile registers for currently-selected device *	into @tf via MMIO. * *	LOCKING: *	Inherited from caller. */static void ata_tf_read_mmio(struct ata_port *ap, struct ata_taskfile *tf){	struct ata_ioports *ioaddr = &ap->ioaddr;	tf->nsect = readb((void __iomem *)ioaddr->nsect_addr);	tf->lbal = readb((void __iomem *)ioaddr->lbal_addr);	tf->lbam = readb((void __iomem *)ioaddr->lbam_addr);	tf->lbah = readb((void __iomem *)ioaddr->lbah_addr);	tf->device = readb((void __iomem *)ioaddr->device_addr);	if (tf->flags & ATA_TFLAG_LBA48) {		writeb(tf->ctl | ATA_HOB, (void __iomem *) ap->ioaddr.ctl_addr);		tf->hob_feature = readb((void __iomem *)ioaddr->error_addr);		tf->hob_nsect = readb((void __iomem *)ioaddr->nsect_addr);		tf->hob_lbal = readb((void __iomem *)ioaddr->lbal_addr);		tf->hob_lbam = readb((void __iomem *)ioaddr->lbam_addr);		tf->hob_lbah = readb((void __iomem *)ioaddr->lbah_addr);	}}void ata_tf_read(struct ata_port *ap, struct ata_taskfile *tf){	if (ap->flags & ATA_FLAG_MMIO)		ata_tf_read_mmio(ap, tf);	else		ata_tf_read_pio(ap, tf);}/** *	ata_check_status - Read device status reg & clear interrupt *	@ap: port where the device is * *	Reads ATA taskfile status register for currently-selected device *	and return it's value. This also clears pending interrupts *      from this device * *	LOCKING: *	Inherited from caller. */static u8 ata_check_status_pio(struct ata_port *ap){	return inb(ap->ioaddr.status_addr);}/** *	ata_check_status_mmio - Read device status reg & clear interrupt *	@ap: port where the device is * *	Reads ATA taskfile status register for currently-selected device *	via MMIO and return it's value. This also clears pending interrupts *      from this device * *	LOCKING: *	Inherited from caller. */static u8 ata_check_status_mmio(struct ata_port *ap){       	return readb((void __iomem *) ap->ioaddr.status_addr);}u8 ata_check_status(struct ata_port *ap){	if (ap->flags & ATA_FLAG_MMIO)		return ata_check_status_mmio(ap);	return ata_check_status_pio(ap);}/** *	ata_tf_to_fis - Convert ATA taskfile to SATA FIS structure *	@tf: Taskfile to convert *	@fis: Buffer into which data will output *	@pmp: Port multiplier port * *	Converts a standard ATA taskfile to a Serial ATA *	FIS structure (Register - Host to Device). * *	LOCKING: *	Inherited from caller. */void ata_tf_to_fis(struct ata_taskfile *tf, u8 *fis, u8 pmp){	fis[0] = 0x27;	/* Register - Host to Device FIS */	fis[1] = (pmp & 0xf) | (1 << 7); /* Port multiplier number,					    bit 7 indicates Command FIS */	fis[2] = tf->command;	fis[3] = tf->feature;	fis[4] = tf->lbal;	fis[5] = tf->lbam;	fis[6] = tf->lbah;	fis[7] = tf->device;	fis[8] = tf->hob_lbal;	fis[9] = tf->hob_lbam;	fis[10] = tf->hob_lbah;	fis[11] = tf->hob_feature;	fis[12] = tf->nsect;	fis[13] = tf->hob_nsect;	fis[14] = 0;	fis[15] = tf->ctl;	fis[16] = 0;	fis[17] = 0;	fis[18] = 0;	fis[19] = 0;}/** *	ata_tf_from_fis - Convert SATA FIS to ATA taskfile *	@fis: Buffer from which data will be input *	@tf: Taskfile to output * *	Converts a standard ATA taskfile to a Serial ATA *	FIS structure (Register - Host to Device). * *	LOCKING: *	Inherited from caller. */void ata_tf_from_fis(u8 *fis, struct ata_taskfile *tf){	tf->command	= fis[2];	/* status */	tf->feature	= fis[3];	/* error */	tf->lbal	= fis[4];	tf->lbam	= fis[5];	tf->lbah	= fis[6];	tf->device	= fis[7];	tf->hob_lbal	= fis[8];	tf->hob_lbam	= fis[9];	tf->hob_lbah	= fis[10];	tf->nsect	= fis[12];	tf->hob_nsect	= fis[13];}/** *	ata_prot_to_cmd - determine which read/write opcodes to use *	@protocol: ATA_PROT_xxx taskfile protocol *	@lba48: true is lba48 is present * *	Given necessary input, determine which read/write commands *	to use to transfer data. * *	LOCKING: *	None. */static int ata_prot_to_cmd(int protocol, int lba48){	int rcmd = 0, wcmd = 0;	switch (protocol) {	case ATA_PROT_PIO:		if (lba48) {			rcmd = ATA_CMD_PIO_READ_EXT;			wcmd = ATA_CMD_PIO_WRITE_EXT;		} else {			rcmd = ATA_CMD_PIO_READ;			wcmd = ATA_CMD_PIO_WRITE;		}		break;	case ATA_PROT_DMA:		if (lba48) {			rcmd = ATA_CMD_READ_EXT;			wcmd = ATA_CMD_WRITE_EXT;		} else {			rcmd = ATA_CMD_READ;			wcmd = ATA_CMD_WRITE;		}		break;	default:		return -1;	}	return rcmd | (wcmd << 8);}/** *	ata_dev_set_protocol - set taskfile protocol and r/w commands *	@dev: device to examine and configure * *	Examine the device configuration, after we have *	read the identify-device page and configured the *	data transfer mode.  Set internal state related to *	the ATA taskfile protocol (pio, pio mult, dma, etc.) *	and calculate the proper read/write commands to use. * *	LOCKING: *	caller. */static void ata_dev_set_protocol(struct ata_device *dev){	int pio = (dev->flags & ATA_DFLAG_PIO);	int lba48 = (dev->flags & ATA_DFLAG_LBA48);	int proto, cmd;	if (pio)		proto = dev->xfer_protocol = ATA_PROT_PIO;	else		proto = dev->xfer_protocol = ATA_PROT_DMA;	cmd = ata_prot_to_cmd(proto, lba48);	if (cmd < 0)		BUG();	dev->read_cmd = cmd & 0xff;	dev->write_cmd = (cmd >> 8) & 0xff;

⌨️ 快捷键说明

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