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

📄 ahci.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
		/* 		 * if we came here with NOT_AVAILABLE, 		 * it just means this is the first time we 		 * have tried to enable - default to max performance, 		 * and let the user go to lower power modes on request. 		 */		ahci_disable_alpm(ap);		return 0;	case MIN_POWER:		/* configure HBA to enter SLUMBER */		asp = PORT_CMD_ASP;		break;	case MEDIUM_POWER:		/* configure HBA to enter PARTIAL */		asp = 0;		break;	default:		return -EINVAL;	}	/* 	 * Disable interrupts on Phy Ready. This keeps us from 	 * getting woken up due to spurious phy ready interrupts	 * TBD - Hot plug should be done via polling now, is	 * that even supported? 	 */	pp->intr_mask &= ~PORT_IRQ_PHYRDY;	writel(pp->intr_mask, port_mmio + PORT_IRQ_MASK);	/* 	 * Set a flag to indicate that we should ignore all PhyRdy 	 * state changes since these can happen now whenever we 	 * change link state 	 */	hpriv->flags |= AHCI_HFLAG_NO_HOTPLUG;	/* get the existing command bits */	cmd = readl(port_mmio + PORT_CMD);	/* 	 * Set ASP based on Policy 	 */	cmd |= asp;	/* 	 * Setting this bit will instruct the HBA to aggressively 	 * enter a lower power link state when it's appropriate and 	 * based on the value set above for ASP 	 */	cmd |= PORT_CMD_ALPE;	/* write out new cmd value */	writel(cmd, port_mmio + PORT_CMD);	cmd = readl(port_mmio + PORT_CMD);	/* IPM bits should be set by libata-core */	return 0;}#ifdef CONFIG_PMstatic void ahci_power_down(struct ata_port *ap){	struct ahci_host_priv *hpriv = ap->host->private_data;	void __iomem *port_mmio = ahci_port_base(ap);	u32 cmd, scontrol;	if (!(hpriv->cap & HOST_CAP_SSS))		return;	/* put device into listen mode, first set PxSCTL.DET to 0 */	scontrol = readl(port_mmio + PORT_SCR_CTL);	scontrol &= ~0xf;	writel(scontrol, port_mmio + PORT_SCR_CTL);	/* then set PxCMD.SUD to 0 */	cmd = readl(port_mmio + PORT_CMD) & ~PORT_CMD_ICC_MASK;	cmd &= ~PORT_CMD_SPIN_UP;	writel(cmd, port_mmio + PORT_CMD);}#endifstatic void ahci_start_port(struct ata_port *ap){	/* enable FIS reception */	ahci_start_fis_rx(ap);	/* enable DMA */	ahci_start_engine(ap);}static int ahci_deinit_port(struct ata_port *ap, const char **emsg){	int rc;	/* disable DMA */	rc = ahci_stop_engine(ap);	if (rc) {		*emsg = "failed to stop engine";		return rc;	}	/* disable FIS reception */	rc = ahci_stop_fis_rx(ap);	if (rc) {		*emsg = "failed stop FIS RX";		return rc;	}	return 0;}static int ahci_reset_controller(struct ata_host *host){	struct pci_dev *pdev = to_pci_dev(host->dev);	void __iomem *mmio = host->iomap[AHCI_PCI_BAR];	u32 tmp;	/* we must be in AHCI mode, before using anything	 * AHCI-specific, such as HOST_RESET.	 */	tmp = readl(mmio + HOST_CTL);	if (!(tmp & HOST_AHCI_EN)) {		tmp |= HOST_AHCI_EN;		writel(tmp, mmio + HOST_CTL);	}	/* global controller reset */	if ((tmp & HOST_RESET) == 0) {		writel(tmp | HOST_RESET, mmio + HOST_CTL);		readl(mmio + HOST_CTL); /* flush */	}	/* reset must complete within 1 second, or	 * the hardware should be considered fried.	 */	ssleep(1);	tmp = readl(mmio + HOST_CTL);	if (tmp & HOST_RESET) {		dev_printk(KERN_ERR, host->dev,			   "controller reset failed (0x%x)\n", tmp);		return -EIO;	}	/* turn on AHCI mode */	writel(HOST_AHCI_EN, mmio + HOST_CTL);	(void) readl(mmio + HOST_CTL);	/* flush */	/* some registers might be cleared on reset.  restore initial values */	ahci_restore_initial_config(host);	if (pdev->vendor == PCI_VENDOR_ID_INTEL) {		u16 tmp16;		/* configure PCS */		pci_read_config_word(pdev, 0x92, &tmp16);		tmp16 |= 0xf;		pci_write_config_word(pdev, 0x92, tmp16);	}	return 0;}static void ahci_port_init(struct pci_dev *pdev, struct ata_port *ap,			   int port_no, void __iomem *mmio,			   void __iomem *port_mmio){	const char *emsg = NULL;	int rc;	u32 tmp;	/* make sure port is not active */	rc = ahci_deinit_port(ap, &emsg);	if (rc)		dev_printk(KERN_WARNING, &pdev->dev,			   "%s (%d)\n", emsg, rc);	/* clear SError */	tmp = readl(port_mmio + PORT_SCR_ERR);	VPRINTK("PORT_SCR_ERR 0x%x\n", tmp);	writel(tmp, port_mmio + PORT_SCR_ERR);	/* clear port IRQ */	tmp = readl(port_mmio + PORT_IRQ_STAT);	VPRINTK("PORT_IRQ_STAT 0x%x\n", tmp);	if (tmp)		writel(tmp, port_mmio + PORT_IRQ_STAT);	writel(1 << port_no, mmio + HOST_IRQ_STAT);}static void ahci_init_controller(struct ata_host *host){	struct ahci_host_priv *hpriv = host->private_data;	struct pci_dev *pdev = to_pci_dev(host->dev);	void __iomem *mmio = host->iomap[AHCI_PCI_BAR];	int i;	void __iomem *port_mmio;	u32 tmp;	if (hpriv->flags & AHCI_HFLAG_MV_PATA) {		port_mmio = __ahci_port_base(host, 4);		writel(0, port_mmio + PORT_IRQ_MASK);		/* clear port IRQ */		tmp = readl(port_mmio + PORT_IRQ_STAT);		VPRINTK("PORT_IRQ_STAT 0x%x\n", tmp);		if (tmp)			writel(tmp, port_mmio + PORT_IRQ_STAT);	}	for (i = 0; i < host->n_ports; i++) {		struct ata_port *ap = host->ports[i];		port_mmio = ahci_port_base(ap);		if (ata_port_is_dummy(ap))			continue;		ahci_port_init(pdev, ap, i, mmio, port_mmio);	}	tmp = readl(mmio + HOST_CTL);	VPRINTK("HOST_CTL 0x%x\n", tmp);	writel(tmp | HOST_IRQ_EN, mmio + HOST_CTL);	tmp = readl(mmio + HOST_CTL);	VPRINTK("HOST_CTL 0x%x\n", tmp);}static unsigned int ahci_dev_classify(struct ata_port *ap){	void __iomem *port_mmio = ahci_port_base(ap);	struct ata_taskfile tf;	u32 tmp;	tmp = readl(port_mmio + PORT_SIG);	tf.lbah		= (tmp >> 24)	& 0xff;	tf.lbam		= (tmp >> 16)	& 0xff;	tf.lbal		= (tmp >> 8)	& 0xff;	tf.nsect	= (tmp)		& 0xff;	return ata_dev_classify(&tf);}static void ahci_fill_cmd_slot(struct ahci_port_priv *pp, unsigned int tag,			       u32 opts){	dma_addr_t cmd_tbl_dma;	cmd_tbl_dma = pp->cmd_tbl_dma + tag * AHCI_CMD_TBL_SZ;	pp->cmd_slot[tag].opts = cpu_to_le32(opts);	pp->cmd_slot[tag].status = 0;	pp->cmd_slot[tag].tbl_addr = cpu_to_le32(cmd_tbl_dma & 0xffffffff);	pp->cmd_slot[tag].tbl_addr_hi = cpu_to_le32((cmd_tbl_dma >> 16) >> 16);}static int ahci_kick_engine(struct ata_port *ap, int force_restart){	void __iomem *port_mmio = ap->ioaddr.cmd_addr;	struct ahci_host_priv *hpriv = ap->host->private_data;	u32 tmp;	int busy, rc;	/* do we need to kick the port? */	busy = ahci_check_status(ap) & (ATA_BUSY | ATA_DRQ);	if (!busy && !force_restart)		return 0;	/* stop engine */	rc = ahci_stop_engine(ap);	if (rc)		goto out_restart;	/* need to do CLO? */	if (!busy) {		rc = 0;		goto out_restart;	}	if (!(hpriv->cap & HOST_CAP_CLO)) {		rc = -EOPNOTSUPP;		goto out_restart;	}	/* perform CLO */	tmp = readl(port_mmio + PORT_CMD);	tmp |= PORT_CMD_CLO;	writel(tmp, port_mmio + PORT_CMD);	rc = 0;	tmp = ata_wait_register(port_mmio + PORT_CMD,				PORT_CMD_CLO, PORT_CMD_CLO, 1, 500);	if (tmp & PORT_CMD_CLO)		rc = -EIO;	/* restart engine */ out_restart:	ahci_start_engine(ap);	return rc;}static int ahci_exec_polled_cmd(struct ata_port *ap, int pmp,				struct ata_taskfile *tf, int is_cmd, u16 flags,				unsigned long timeout_msec){	const u32 cmd_fis_len = 5; /* five dwords */	struct ahci_port_priv *pp = ap->private_data;	void __iomem *port_mmio = ahci_port_base(ap);	u8 *fis = pp->cmd_tbl;	u32 tmp;	/* prep the command */	ata_tf_to_fis(tf, pmp, is_cmd, fis);	ahci_fill_cmd_slot(pp, 0, cmd_fis_len | flags | (pmp << 12));	/* issue & wait */	writel(1, port_mmio + PORT_CMD_ISSUE);	if (timeout_msec) {		tmp = ata_wait_register(port_mmio + PORT_CMD_ISSUE, 0x1, 0x1,					1, timeout_msec);		if (tmp & 0x1) {			ahci_kick_engine(ap, 1);			return -EBUSY;		}	} else		readl(port_mmio + PORT_CMD_ISSUE);	/* flush */	return 0;}static int ahci_do_softreset(struct ata_link *link, unsigned int *class,			     int pmp, unsigned long deadline){	struct ata_port *ap = link->ap;	const char *reason = NULL;	unsigned long now, msecs;	struct ata_taskfile tf;	int rc;	DPRINTK("ENTER\n");	if (ata_link_offline(link)) {		DPRINTK("PHY reports no device\n");		*class = ATA_DEV_NONE;		return 0;	}	/* prepare for SRST (AHCI-1.1 10.4.1) */	rc = ahci_kick_engine(ap, 1);	if (rc && rc != -EOPNOTSUPP)		ata_link_printk(link, KERN_WARNING,				"failed to reset engine (errno=%d)\n", rc);	ata_tf_init(link->device, &tf);	/* issue the first D2H Register FIS */	msecs = 0;	now = jiffies;	if (time_after(now, deadline))		msecs = jiffies_to_msecs(deadline - now);	tf.ctl |= ATA_SRST;	if (ahci_exec_polled_cmd(ap, pmp, &tf, 0,				 AHCI_CMD_RESET | AHCI_CMD_CLR_BUSY, msecs)) {		rc = -EIO;		reason = "1st FIS failed";		goto fail;	}	/* spec says at least 5us, but be generous and sleep for 1ms */	msleep(1);	/* issue the second D2H Register FIS */	tf.ctl &= ~ATA_SRST;	ahci_exec_polled_cmd(ap, pmp, &tf, 0, 0, 0);	/* wait a while before checking status */	ata_wait_after_reset(ap, deadline);	rc = ata_wait_ready(ap, deadline);	/* link occupied, -ENODEV too is an error */	if (rc) {		reason = "device not ready";		goto fail;	}	*class = ahci_dev_classify(ap);	DPRINTK("EXIT, class=%u\n", *class);	return 0; fail:	ata_link_printk(link, KERN_ERR, "softreset failed (%s)\n", reason);	return rc;}static int ahci_softreset(struct ata_link *link, unsigned int *class,			  unsigned long deadline){	int pmp = 0;	if (link->ap->flags & ATA_FLAG_PMP)		pmp = SATA_PMP_CTRL_PORT;	return ahci_do_softreset(link, class, pmp, deadline);}static int ahci_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;	DPRINTK("ENTER\n");	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_std_hardreset(link, class, deadline);	ahci_start_engine(ap);	if (rc == 0 && ata_link_online(link))		*class = ahci_dev_classify(ap);	if (rc != -EAGAIN && *class == ATA_DEV_UNKNOWN)		*class = ATA_DEV_NONE;	DPRINTK("EXIT, rc=%d, class=%u\n", rc, *class);	return rc;}static int ahci_vt8251_hardreset(struct ata_link *link, unsigned int *class,				 unsigned long deadline){	struct ata_port *ap = link->ap;	u32 serror;	int rc;	DPRINTK("ENTER\n");	ahci_stop_engine(ap);	rc = sata_link_hardreset(link, sata_ehc_deb_timing(&link->eh_context),				 deadline);	/* vt8251 needs SError cleared for the port to operate */	ahci_scr_read(ap, SCR_ERROR, &serror);	ahci_scr_write(ap, SCR_ERROR, serror);	ahci_start_engine(ap);	DPRINTK("EXIT, rc=%d, class=%u\n", rc, *class);	/* vt8251 doesn't clear BSY on signature FIS reception,

⌨️ 快捷键说明

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