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

📄 ahci.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
	 * request follow-up softreset.	 */	return rc ?: -EAGAIN;}static int ahci_p5wdh_hardreset(struct ata_link *link, unsigned int *class,				unsigned long deadline){	struct ata_port *ap = link->ap;	struct ahci_port_priv *pp = ap->private_data;	u8 *d2h_fis = pp->rx_fis + RX_FIS_D2H_REG;	struct ata_taskfile tf;	int rc;	ahci_stop_engine(ap);	/* clear D2H reception area to properly wait for D2H FIS */	ata_tf_init(link->device, &tf);	tf.command = 0x80;	ata_tf_to_fis(&tf, 0, 0, d2h_fis);	rc = sata_link_hardreset(link, sata_ehc_deb_timing(&link->eh_context),				 deadline);	ahci_start_engine(ap);	if (rc || ata_link_offline(link))		return rc;	/* spec mandates ">= 2ms" before checking status */	msleep(150);	/* The pseudo configuration device on SIMG4726 attached to	 * ASUS P5W-DH Deluxe doesn't send signature FIS after	 * hardreset if no device is attached to the first downstream	 * port && the pseudo device locks up on SRST w/ PMP==0.  To	 * work around this, wait for !BSY only briefly.  If BSY isn't	 * cleared, perform CLO and proceed to IDENTIFY (achieved by	 * ATA_LFLAG_NO_SRST and ATA_LFLAG_ASSUME_ATA).	 *	 * Wait for two seconds.  Devices attached to downstream port	 * which can't process the following IDENTIFY after this will	 * have to be reset again.  For most cases, this should	 * suffice while making probing snappish enough.	 */	rc = ata_wait_ready(ap, jiffies + 2 * HZ);	if (rc)		ahci_kick_engine(ap, 0);	return 0;}static void ahci_postreset(struct ata_link *link, unsigned int *class){	struct ata_port *ap = link->ap;	void __iomem *port_mmio = ahci_port_base(ap);	u32 new_tmp, tmp;	ata_std_postreset(link, class);	/* Make sure port's ATAPI bit is set appropriately */	new_tmp = tmp = readl(port_mmio + PORT_CMD);	if (*class == ATA_DEV_ATAPI)		new_tmp |= PORT_CMD_ATAPI;	else		new_tmp &= ~PORT_CMD_ATAPI;	if (new_tmp != tmp) {		writel(new_tmp, port_mmio + PORT_CMD);		readl(port_mmio + PORT_CMD); /* flush */	}}static int ahci_pmp_softreset(struct ata_link *link, unsigned int *class,			      unsigned long deadline){	return ahci_do_softreset(link, class, link->pmp, deadline);}static u8 ahci_check_status(struct ata_port *ap){	void __iomem *mmio = ap->ioaddr.cmd_addr;	return readl(mmio + PORT_TFDATA) & 0xFF;}static void ahci_tf_read(struct ata_port *ap, struct ata_taskfile *tf){	struct ahci_port_priv *pp = ap->private_data;	u8 *d2h_fis = pp->rx_fis + RX_FIS_D2H_REG;	ata_tf_from_fis(d2h_fis, tf);}static unsigned int ahci_fill_sg(struct ata_queued_cmd *qc, void *cmd_tbl){	struct scatterlist *sg;	struct ahci_sg *ahci_sg;	unsigned int n_sg = 0;	VPRINTK("ENTER\n");	/*	 * Next, the S/G list.	 */	ahci_sg = cmd_tbl + AHCI_CMD_TBL_HDR_SZ;	ata_for_each_sg(sg, qc) {		dma_addr_t addr = sg_dma_address(sg);		u32 sg_len = sg_dma_len(sg);		ahci_sg->addr = cpu_to_le32(addr & 0xffffffff);		ahci_sg->addr_hi = cpu_to_le32((addr >> 16) >> 16);		ahci_sg->flags_size = cpu_to_le32(sg_len - 1);		ahci_sg++;		n_sg++;	}	return n_sg;}static void ahci_qc_prep(struct ata_queued_cmd *qc){	struct ata_port *ap = qc->ap;	struct ahci_port_priv *pp = ap->private_data;	int is_atapi = is_atapi_taskfile(&qc->tf);	void *cmd_tbl;	u32 opts;	const u32 cmd_fis_len = 5; /* five dwords */	unsigned int n_elem;	/*	 * Fill in command table information.  First, the header,	 * a SATA Register - Host to Device command FIS.	 */	cmd_tbl = pp->cmd_tbl + qc->tag * AHCI_CMD_TBL_SZ;	ata_tf_to_fis(&qc->tf, qc->dev->link->pmp, 1, cmd_tbl);	if (is_atapi) {		memset(cmd_tbl + AHCI_CMD_TBL_CDB, 0, 32);		memcpy(cmd_tbl + AHCI_CMD_TBL_CDB, qc->cdb, qc->dev->cdb_len);	}	n_elem = 0;	if (qc->flags & ATA_QCFLAG_DMAMAP)		n_elem = ahci_fill_sg(qc, cmd_tbl);	/*	 * Fill in command slot information.	 */	opts = cmd_fis_len | n_elem << 16 | (qc->dev->link->pmp << 12);	if (qc->tf.flags & ATA_TFLAG_WRITE)		opts |= AHCI_CMD_WRITE;	if (is_atapi)		opts |= AHCI_CMD_ATAPI | AHCI_CMD_PREFETCH;	ahci_fill_cmd_slot(pp, qc->tag, opts);}static void ahci_error_intr(struct ata_port *ap, u32 irq_stat){	struct ahci_host_priv *hpriv = ap->host->private_data;	struct ahci_port_priv *pp = ap->private_data;	struct ata_eh_info *host_ehi = &ap->link.eh_info;	struct ata_link *link = NULL;	struct ata_queued_cmd *active_qc;	struct ata_eh_info *active_ehi;	u32 serror;	/* determine active link */	ata_port_for_each_link(link, ap)		if (ata_link_active(link))			break;	if (!link)		link = &ap->link;	active_qc = ata_qc_from_tag(ap, link->active_tag);	active_ehi = &link->eh_info;	/* record irq stat */	ata_ehi_clear_desc(host_ehi);	ata_ehi_push_desc(host_ehi, "irq_stat 0x%08x", irq_stat);	/* AHCI needs SError cleared; otherwise, it might lock up */	ahci_scr_read(ap, SCR_ERROR, &serror);	ahci_scr_write(ap, SCR_ERROR, serror);	host_ehi->serror |= serror;	/* some controllers set IRQ_IF_ERR on device errors, ignore it */	if (hpriv->flags & AHCI_HFLAG_IGN_IRQ_IF_ERR)		irq_stat &= ~PORT_IRQ_IF_ERR;	if (irq_stat & PORT_IRQ_TF_ERR) {		/* If qc is active, charge it; otherwise, the active		 * link.  There's no active qc on NCQ errors.  It will		 * be determined by EH by reading log page 10h.		 */		if (active_qc)			active_qc->err_mask |= AC_ERR_DEV;		else			active_ehi->err_mask |= AC_ERR_DEV;		if (hpriv->flags & AHCI_HFLAG_IGN_SERR_INTERNAL)			host_ehi->serror &= ~SERR_INTERNAL;	}	if (irq_stat & PORT_IRQ_UNK_FIS) {		u32 *unk = (u32 *)(pp->rx_fis + RX_FIS_UNK);		active_ehi->err_mask |= AC_ERR_HSM;		active_ehi->action |= ATA_EH_SOFTRESET;		ata_ehi_push_desc(active_ehi,				  "unknown FIS %08x %08x %08x %08x" ,				  unk[0], unk[1], unk[2], unk[3]);	}	if (ap->nr_pmp_links && (irq_stat & PORT_IRQ_BAD_PMP)) {		active_ehi->err_mask |= AC_ERR_HSM;		active_ehi->action |= ATA_EH_SOFTRESET;		ata_ehi_push_desc(active_ehi, "incorrect PMP");	}	if (irq_stat & (PORT_IRQ_HBUS_ERR | PORT_IRQ_HBUS_DATA_ERR)) {		host_ehi->err_mask |= AC_ERR_HOST_BUS;		host_ehi->action |= ATA_EH_SOFTRESET;		ata_ehi_push_desc(host_ehi, "host bus error");	}	if (irq_stat & PORT_IRQ_IF_ERR) {		host_ehi->err_mask |= AC_ERR_ATA_BUS;		host_ehi->action |= ATA_EH_SOFTRESET;		ata_ehi_push_desc(host_ehi, "interface fatal error");	}	if (irq_stat & (PORT_IRQ_CONNECT | PORT_IRQ_PHYRDY)) {		ata_ehi_hotplugged(host_ehi);		ata_ehi_push_desc(host_ehi, "%s",			irq_stat & PORT_IRQ_CONNECT ?			"connection status changed" : "PHY RDY changed");	}	/* okay, let's hand over to EH */	if (irq_stat & PORT_IRQ_FREEZE)		ata_port_freeze(ap);	else		ata_port_abort(ap);}static void ahci_port_intr(struct ata_port *ap){	void __iomem *port_mmio = ap->ioaddr.cmd_addr;	struct ata_eh_info *ehi = &ap->link.eh_info;	struct ahci_port_priv *pp = ap->private_data;	struct ahci_host_priv *hpriv = ap->host->private_data;	int resetting = !!(ap->pflags & ATA_PFLAG_RESETTING);	u32 status, qc_active;	int rc;	status = readl(port_mmio + PORT_IRQ_STAT);	writel(status, port_mmio + PORT_IRQ_STAT);	/* ignore BAD_PMP while resetting */	if (unlikely(resetting))		status &= ~PORT_IRQ_BAD_PMP;	/* If we are getting PhyRdy, this is 	 * just a power state change, we should 	 * clear out this, plus the PhyRdy/Comm 	 * Wake bits from Serror 	 */	if ((hpriv->flags & AHCI_HFLAG_NO_HOTPLUG) &&		(status & PORT_IRQ_PHYRDY)) {		status &= ~PORT_IRQ_PHYRDY;		ahci_scr_write(ap, SCR_ERROR, ((1 << 16) | (1 << 18)));	}	if (unlikely(status & PORT_IRQ_ERROR)) {		ahci_error_intr(ap, status);		return;	}	if (status & PORT_IRQ_SDB_FIS) {		/* If SNotification is available, leave notification		 * handling to sata_async_notification().  If not,		 * emulate it by snooping SDB FIS RX area.		 *		 * Snooping FIS RX area is probably cheaper than		 * poking SNotification but some constrollers which		 * implement SNotification, ICH9 for example, don't		 * store AN SDB FIS into receive area.		 */		if (hpriv->cap & HOST_CAP_SNTF)			sata_async_notification(ap);		else {			/* If the 'N' bit in word 0 of the FIS is set,			 * we just received asynchronous notification.			 * Tell libata about it.			 */			const __le32 *f = pp->rx_fis + RX_FIS_SDB;			u32 f0 = le32_to_cpu(f[0]);			if (f0 & (1 << 15))				sata_async_notification(ap);		}	}	/* pp->active_link is valid iff any command is in flight */	if (ap->qc_active && pp->active_link->sactive)		qc_active = readl(port_mmio + PORT_SCR_ACT);	else		qc_active = readl(port_mmio + PORT_CMD_ISSUE);	rc = ata_qc_complete_multiple(ap, qc_active, NULL);	/* while resetting, invalid completions are expected */	if (unlikely(rc < 0 && !resetting)) {		ehi->err_mask |= AC_ERR_HSM;		ehi->action |= ATA_EH_SOFTRESET;		ata_port_freeze(ap);	}}static void ahci_irq_clear(struct ata_port *ap){	/* TODO */}static irqreturn_t ahci_interrupt(int irq, void *dev_instance){	struct ata_host *host = dev_instance;	struct ahci_host_priv *hpriv;	unsigned int i, handled = 0;	void __iomem *mmio;	u32 irq_stat, irq_ack = 0;	VPRINTK("ENTER\n");	hpriv = host->private_data;	mmio = host->iomap[AHCI_PCI_BAR];	/* sigh.  0xffffffff is a valid return from h/w */	irq_stat = readl(mmio + HOST_IRQ_STAT);	irq_stat &= hpriv->port_map;	if (!irq_stat)		return IRQ_NONE;	spin_lock(&host->lock);	for (i = 0; i < host->n_ports; i++) {		struct ata_port *ap;		if (!(irq_stat & (1 << i)))			continue;		ap = host->ports[i];		if (ap) {			ahci_port_intr(ap);			VPRINTK("port %u\n", i);		} else {			VPRINTK("port %u (no irq)\n", i);			if (ata_ratelimit())				dev_printk(KERN_WARNING, host->dev,					"interrupt on disabled port %u\n", i);		}		irq_ack |= (1 << i);	}	if (irq_ack) {		writel(irq_ack, mmio + HOST_IRQ_STAT);		handled = 1;	}	spin_unlock(&host->lock);	VPRINTK("EXIT\n");	return IRQ_RETVAL(handled);}static unsigned int ahci_qc_issue(struct ata_queued_cmd *qc){	struct ata_port *ap = qc->ap;	void __iomem *port_mmio = ahci_port_base(ap);	struct ahci_port_priv *pp = ap->private_data;	/* Keep track of the currently active link.  It will be used	 * in completion path to determine whether NCQ phase is in	 * progress.	 */	pp->active_link = qc->dev->link;	if (qc->tf.protocol == ATA_PROT_NCQ)		writel(1 << qc->tag, port_mmio + PORT_SCR_ACT);	writel(1 << qc->tag, port_mmio + PORT_CMD_ISSUE);	readl(port_mmio + PORT_CMD_ISSUE);	/* flush */	return 0;}static void ahci_freeze(struct ata_port *ap){	void __iomem *port_mmio = ahci_port_base(ap);	/* turn IRQ off */	writel(0, port_mmio + PORT_IRQ_MASK);}static void ahci_thaw(struct ata_port *ap){	void __iomem *mmio = ap->host->iomap[AHCI_PCI_BAR];	void __iomem *port_mmio = ahci_port_base(ap);	u32 tmp;	struct ahci_port_priv *pp = ap->private_data;	/* clear IRQ */	tmp = readl(port_mmio + PORT_IRQ_STAT);	writel(tmp, port_mmio + PORT_IRQ_STAT);	writel(1 << ap->port_no, mmio + HOST_IRQ_STAT);	/* turn IRQ back on */	writel(pp->intr_mask, port_mmio + PORT_IRQ_MASK);}static void ahci_error_handler(struct ata_port *ap){	if (!(ap->pflags & ATA_PFLAG_FROZEN)) {		/* restart engine */		ahci_stop_engine(ap);		ahci_start_engine(ap);	}	/* perform recovery */	sata_pmp_do_eh(ap, ata_std_prereset, ahci_softreset,		       ahci_hardreset, ahci_postreset,		       sata_pmp_std_prereset, ahci_pmp_softreset,		       sata_pmp_std_hardreset, sata_pmp_std_postreset);}static void ahci_vt8251_error_handler(struct ata_port *ap){	if (!(ap->pflags & ATA_PFLAG_FROZEN)) {		/* restart engine */		ahci_stop_engine(ap);		ahci_start_engine(ap);	}	/* perform recovery */	ata_do_eh(ap, ata_std_prereset, ahci_softreset, ahci_vt8251_hardreset,		  ahci_postreset);}static void ahci_p5wdh_error_handler(struct ata_port *ap){	if (!(ap->pflags & ATA_PFLAG_FROZEN)) {		/* restart engine */		ahci_stop_engine(ap);		ahci_start_engine(ap);	}	/* perform recovery */	ata_do_eh(ap, ata_std_prereset, ahci_softreset, ahci_p5wdh_hardreset,

⌨️ 快捷键说明

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