3217 lines · 80.2 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-or-later/* *  libata-sff.c - helper library for PCI IDE BMDMA * *  Copyright 2003-2006 Red Hat, Inc.  All rights reserved. *  Copyright 2003-2006 Jeff Garzik * *  libata documentation is available via 'make {ps|pdf}docs', *  as Documentation/driver-api/libata.rst * *  Hardware documentation available from http://www.t13.org/ and *  http://www.sata-io.org/ */ #include <linux/kernel.h>#include <linux/gfp.h>#include <linux/pci.h>#include <linux/module.h>#include <linux/libata.h>#include <linux/highmem.h>#include <trace/events/libata.h>#include "libata.h" static struct workqueue_struct *ata_sff_wq; const struct ata_port_operations ata_sff_port_ops = {	.inherits		= &ata_base_port_ops, 	.qc_issue		= ata_sff_qc_issue,	.qc_fill_rtf		= ata_sff_qc_fill_rtf, 	.freeze			= ata_sff_freeze,	.thaw			= ata_sff_thaw,	.reset.prereset		= ata_sff_prereset,	.reset.softreset	= ata_sff_softreset,	.reset.hardreset	= sata_sff_hardreset,	.reset.postreset	= ata_sff_postreset,	.error_handler		= ata_sff_error_handler, 	.sff_dev_select		= ata_sff_dev_select,	.sff_check_status	= ata_sff_check_status,	.sff_tf_load		= ata_sff_tf_load,	.sff_tf_read		= ata_sff_tf_read,	.sff_exec_command	= ata_sff_exec_command,	.sff_data_xfer		= ata_sff_data_xfer,	.sff_drain_fifo		= ata_sff_drain_fifo, 	.lost_interrupt		= ata_sff_lost_interrupt,};EXPORT_SYMBOL_GPL(ata_sff_port_ops); /** *	ata_sff_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 its value. This also clears pending interrupts *      from this device * *	LOCKING: *	Inherited from caller. */u8 ata_sff_check_status(struct ata_port *ap){	return ioread8(ap->ioaddr.status_addr);}EXPORT_SYMBOL_GPL(ata_sff_check_status); /** *	ata_sff_altstatus - Read device alternate status reg *	@ap: port where the device is *	@status: pointer to a status value * *	Reads ATA alternate status register for currently-selected device *	and return its value. * *	RETURN: *	true if the register exists, false if not. * *	LOCKING: *	Inherited from caller. */static bool ata_sff_altstatus(struct ata_port *ap, u8 *status){	u8 tmp; 	if (ap->ops->sff_check_altstatus) {		tmp = ap->ops->sff_check_altstatus(ap);		goto read;	}	if (ap->ioaddr.altstatus_addr) {		tmp = ioread8(ap->ioaddr.altstatus_addr);		goto read;	}	return false; read:	if (status)		*status = tmp;	return true;} /** *	ata_sff_irq_status - Check if the device is busy *	@ap: port where the device is * *	Determine if the port is currently busy. Uses altstatus *	if available in order to avoid clearing shared IRQ status *	when finding an IRQ source. Non ctl capable devices don't *	share interrupt lines fortunately for us. * *	LOCKING: *	Inherited from caller. */static u8 ata_sff_irq_status(struct ata_port *ap){	u8 status; 	/* Not us: We are busy */	if (ata_sff_altstatus(ap, &status) && (status & ATA_BUSY))		return status;	/* Clear INTRQ latch */	status = ap->ops->sff_check_status(ap);	return status;} /** *	ata_sff_sync - Flush writes *	@ap: Port to wait for. * *	CAUTION: *	If we have an mmio device with no ctl and no altstatus *	method this will fail. No such devices are known to exist. * *	LOCKING: *	Inherited from caller. */ static void ata_sff_sync(struct ata_port *ap){	ata_sff_altstatus(ap, NULL);} /** *	ata_sff_pause		-	Flush writes and wait 400nS *	@ap: Port to pause for. * *	CAUTION: *	If we have an mmio device with no ctl and no altstatus *	method this will fail. No such devices are known to exist. * *	LOCKING: *	Inherited from caller. */ void ata_sff_pause(struct ata_port *ap){	ata_sff_sync(ap);	ndelay(400);}EXPORT_SYMBOL_GPL(ata_sff_pause); /** *	ata_sff_dma_pause	-	Pause before commencing DMA *	@ap: Port to pause for. * *	Perform I/O fencing and ensure sufficient cycle delays occur *	for the HDMA1:0 transition */ void ata_sff_dma_pause(struct ata_port *ap){	/*	 * An altstatus read will cause the needed delay without	 * messing up the IRQ status	 */	if (ata_sff_altstatus(ap, NULL))		return;	/* There are no DMA controllers without ctl. BUG here to ensure	   we never violate the HDMA1:0 transition timing and risk	   corruption. */	BUG();}EXPORT_SYMBOL_GPL(ata_sff_dma_pause); static int ata_sff_check_ready(struct ata_link *link){	u8 status = link->ap->ops->sff_check_status(link->ap); 	return ata_check_ready(status);} /** *	ata_sff_wait_ready - sleep until BSY clears, or timeout *	@link: SFF link to wait ready status for *	@deadline: deadline jiffies for the operation * *	Sleep until ATA Status register bit BSY clears, or timeout *	occurs. * *	LOCKING: *	Kernel thread context (may sleep). * *	RETURNS: *	0 on success, -errno otherwise. */int ata_sff_wait_ready(struct ata_link *link, unsigned long deadline){	return ata_wait_ready(link, deadline, ata_sff_check_ready);}EXPORT_SYMBOL_GPL(ata_sff_wait_ready); /** *	ata_sff_set_devctl - Write device control reg *	@ap: port where the device is *	@ctl: value to write * *	Writes ATA device control register. * *	RETURN: *	true if the register exists, false if not. * *	LOCKING: *	Inherited from caller. */static bool ata_sff_set_devctl(struct ata_port *ap, u8 ctl){	if (ap->ops->sff_set_devctl) {		ap->ops->sff_set_devctl(ap, ctl);		return true;	}	if (ap->ioaddr.ctl_addr) {		iowrite8(ctl, ap->ioaddr.ctl_addr);		return true;	} 	return false;} /** *	ata_sff_dev_select - Select device 0/1 on ATA bus *	@ap: ATA channel to manipulate *	@device: ATA device (numbered from zero) to select * *	Use the method defined in the ATA specification to *	make either device 0, or device 1, active on the *	ATA channel.  Works with both PIO and MMIO. * *	May be used as the dev_select() entry in ata_port_operations. * *	LOCKING: *	caller. */void ata_sff_dev_select(struct ata_port *ap, unsigned int device){	u8 tmp; 	if (device == 0)		tmp = ATA_DEVICE_OBS;	else		tmp = ATA_DEVICE_OBS | ATA_DEV1; 	iowrite8(tmp, ap->ioaddr.device_addr);	ata_sff_pause(ap);	/* needed; also flushes, for mmio */}EXPORT_SYMBOL_GPL(ata_sff_dev_select); /** *	ata_dev_select - Select device 0/1 on ATA bus *	@ap: ATA channel to manipulate *	@device: ATA device (numbered from zero) to select *	@wait: non-zero to wait for Status register BSY bit to clear *	@can_sleep: non-zero if context allows sleeping * *	Use the method defined in the ATA specification to *	make either device 0, or device 1, active on the *	ATA channel. * *	This is a high-level version of ata_sff_dev_select(), which *	additionally provides the services of inserting the proper *	pauses and status polling, where needed. * *	LOCKING: *	caller. */static void ata_dev_select(struct ata_port *ap, unsigned int device,			   unsigned int wait, unsigned int can_sleep){	if (wait)		ata_wait_idle(ap); 	ap->ops->sff_dev_select(ap, device); 	if (wait) {		if (can_sleep && ap->link.device[device].class == ATA_DEV_ATAPI)			ata_msleep(ap, 150);		ata_wait_idle(ap);	}} /** *	ata_sff_irq_on - Enable interrupts on a port. *	@ap: Port on which interrupts are enabled. * *	Enable interrupts on a legacy IDE device using MMIO or PIO, *	wait for idle, clear any pending interrupts. * *	Note: may NOT be used as the sff_irq_on() entry in *	ata_port_operations. * *	LOCKING: *	Inherited from caller. */void ata_sff_irq_on(struct ata_port *ap){	if (ap->ops->sff_irq_on) {		ap->ops->sff_irq_on(ap);		return;	} 	ap->ctl &= ~ATA_NIEN;	ap->last_ctl = ap->ctl; 	ata_sff_set_devctl(ap, ap->ctl);	ata_wait_idle(ap); 	if (ap->ops->sff_irq_clear)		ap->ops->sff_irq_clear(ap);}EXPORT_SYMBOL_GPL(ata_sff_irq_on); /** *	ata_sff_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. */void ata_sff_tf_load(struct ata_port *ap, const 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) {		if (ioaddr->ctl_addr)			iowrite8(tf->ctl, ioaddr->ctl_addr);		ap->last_ctl = tf->ctl;		ata_wait_idle(ap);	} 	if (is_addr && (tf->flags & ATA_TFLAG_LBA48)) {		WARN_ON_ONCE(!ioaddr->ctl_addr);		iowrite8(tf->hob_feature, ioaddr->feature_addr);		iowrite8(tf->hob_nsect, ioaddr->nsect_addr);		iowrite8(tf->hob_lbal, ioaddr->lbal_addr);		iowrite8(tf->hob_lbam, ioaddr->lbam_addr);		iowrite8(tf->hob_lbah, ioaddr->lbah_addr);	} 	if (is_addr) {		iowrite8(tf->feature, ioaddr->feature_addr);		iowrite8(tf->nsect, ioaddr->nsect_addr);		iowrite8(tf->lbal, ioaddr->lbal_addr);		iowrite8(tf->lbam, ioaddr->lbam_addr);		iowrite8(tf->lbah, ioaddr->lbah_addr);	} 	if (tf->flags & ATA_TFLAG_DEVICE)		iowrite8(tf->device, ioaddr->device_addr); 	ata_wait_idle(ap);}EXPORT_SYMBOL_GPL(ata_sff_tf_load); /** *	ata_sff_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. Assumes the device has a fully SFF compliant task file *	layout and behaviour. If you device does not (eg has a different *	status method) then you will need to provide a replacement tf_read * *	LOCKING: *	Inherited from caller. */void ata_sff_tf_read(struct ata_port *ap, struct ata_taskfile *tf){	struct ata_ioports *ioaddr = &ap->ioaddr; 	tf->status = ata_sff_check_status(ap);	tf->error = ioread8(ioaddr->error_addr);	tf->nsect = ioread8(ioaddr->nsect_addr);	tf->lbal = ioread8(ioaddr->lbal_addr);	tf->lbam = ioread8(ioaddr->lbam_addr);	tf->lbah = ioread8(ioaddr->lbah_addr);	tf->device = ioread8(ioaddr->device_addr); 	if (tf->flags & ATA_TFLAG_LBA48) {		if (likely(ioaddr->ctl_addr)) {			iowrite8(tf->ctl | ATA_HOB, ioaddr->ctl_addr);			tf->hob_feature = ioread8(ioaddr->error_addr);			tf->hob_nsect = ioread8(ioaddr->nsect_addr);			tf->hob_lbal = ioread8(ioaddr->lbal_addr);			tf->hob_lbam = ioread8(ioaddr->lbam_addr);			tf->hob_lbah = ioread8(ioaddr->lbah_addr);			iowrite8(tf->ctl, ioaddr->ctl_addr);			ap->last_ctl = tf->ctl;		} else			WARN_ON_ONCE(1);	}}EXPORT_SYMBOL_GPL(ata_sff_tf_read); /** *	ata_sff_exec_command - issue ATA command to host controller *	@ap: port to which command is being issued *	@tf: ATA taskfile register set * *	Issues ATA command, with proper synchronization with interrupt *	handler / other threads. * *	LOCKING: *	spin_lock_irqsave(host lock) */void ata_sff_exec_command(struct ata_port *ap, const struct ata_taskfile *tf){	iowrite8(tf->command, ap->ioaddr.command_addr);	ata_sff_pause(ap);}EXPORT_SYMBOL_GPL(ata_sff_exec_command); /** *	ata_tf_to_host - issue ATA taskfile to host controller *	@ap: port to which command is being issued *	@tf: ATA taskfile register set *	@tag: tag of the associated command * *	Issues ATA taskfile register set to ATA host controller, *	with proper synchronization with interrupt handler and *	other threads. * *	LOCKING: *	spin_lock_irqsave(host lock) */static inline void ata_tf_to_host(struct ata_port *ap,				  const struct ata_taskfile *tf,				  unsigned int tag){	trace_ata_tf_load(ap, tf);	ap->ops->sff_tf_load(ap, tf);	trace_ata_exec_command(ap, tf, tag);	ap->ops->sff_exec_command(ap, tf);} /** *	ata_sff_data_xfer - Transfer data by PIO *	@qc: queued command *	@buf: data buffer *	@buflen: buffer length *	@rw: read/write * *	Transfer data from/to the device data register by PIO. * *	LOCKING: *	Inherited from caller. * *	RETURNS: *	Bytes consumed. */unsigned int ata_sff_data_xfer(struct ata_queued_cmd *qc, unsigned char *buf,			       unsigned int buflen, int rw){	struct ata_port *ap = qc->dev->link->ap;	void __iomem *data_addr = ap->ioaddr.data_addr;	unsigned int words = buflen >> 1; 	/* Transfer multiple of 2 bytes */	if (rw == READ)		ioread16_rep(data_addr, buf, words);	else		iowrite16_rep(data_addr, buf, words); 	/* Transfer trailing byte, if any. */	if (unlikely(buflen & 0x01)) {		unsigned char pad[2] = { }; 		/* Point buf to the tail of buffer */		buf += buflen - 1; 		/*		 * Use io*16_rep() accessors here as well to avoid pointlessly		 * swapping bytes to and from on the big endian machines...		 */		if (rw == READ) {			ioread16_rep(data_addr, pad, 1);

Lines 1–500 of 3217.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t