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

📄 sata_sil24.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 3 页
字号:
	if (rc) {		ata_link_printk(link, KERN_ERR,				"hardreset failed (port not ready)\n");		return rc;	}	return sata_pmp_std_hardreset(link, class, deadline);}static void sil24_freeze(struct ata_port *ap){	void __iomem *port = ap->ioaddr.cmd_addr;	/* Port-wide IRQ mask in HOST_CTRL doesn't really work, clear	 * PORT_IRQ_ENABLE instead.	 */	writel(0xffff, port + PORT_IRQ_ENABLE_CLR);}static void sil24_thaw(struct ata_port *ap){	void __iomem *port = ap->ioaddr.cmd_addr;	u32 tmp;	/* clear IRQ */	tmp = readl(port + PORT_IRQ_STAT);	writel(tmp, port + PORT_IRQ_STAT);	/* turn IRQ back on */	writel(DEF_PORT_IRQ, port + PORT_IRQ_ENABLE_SET);}static void sil24_error_intr(struct ata_port *ap){	void __iomem *port = ap->ioaddr.cmd_addr;	struct sil24_port_priv *pp = ap->private_data;	struct ata_queued_cmd *qc = NULL;	struct ata_link *link;	struct ata_eh_info *ehi;	int abort = 0, freeze = 0;	u32 irq_stat;	/* on error, we need to clear IRQ explicitly */	irq_stat = readl(port + PORT_IRQ_STAT);	writel(irq_stat, port + PORT_IRQ_STAT);	/* first, analyze and record host port events */	link = &ap->link;	ehi = &link->eh_info;	ata_ehi_clear_desc(ehi);	ata_ehi_push_desc(ehi, "irq_stat 0x%08x", irq_stat);	if (irq_stat & PORT_IRQ_SDB_NOTIFY) {		ata_ehi_push_desc(ehi, "SDB notify");		sata_async_notification(ap);	}	if (irq_stat & (PORT_IRQ_PHYRDY_CHG | PORT_IRQ_DEV_XCHG)) {		ata_ehi_hotplugged(ehi);		ata_ehi_push_desc(ehi, "%s",				  irq_stat & PORT_IRQ_PHYRDY_CHG ?				  "PHY RDY changed" : "device exchanged");		freeze = 1;	}	if (irq_stat & PORT_IRQ_UNK_FIS) {		ehi->err_mask |= AC_ERR_HSM;		ehi->action |= ATA_EH_SOFTRESET;		ata_ehi_push_desc(ehi, "unknown FIS");		freeze = 1;	}	/* deal with command error */	if (irq_stat & PORT_IRQ_ERROR) {		struct sil24_cerr_info *ci = NULL;		unsigned int err_mask = 0, action = 0;		u32 context, cerr;		int pmp;		abort = 1;		/* DMA Context Switch Failure in Port Multiplier Mode		 * errata.  If we have active commands to 3 or more		 * devices, any error condition on active devices can		 * corrupt DMA context switching.		 */		if (ap->nr_active_links >= 3) {			ehi->err_mask |= AC_ERR_OTHER;			ehi->action |= ATA_EH_HARDRESET;			ata_ehi_push_desc(ehi, "PMP DMA CS errata");			pp->do_port_rst = 1;			freeze = 1;		}		/* find out the offending link and qc */		if (ap->nr_pmp_links) {			context = readl(port + PORT_CONTEXT);			pmp = (context >> 5) & 0xf;			if (pmp < ap->nr_pmp_links) {				link = &ap->pmp_link[pmp];				ehi = &link->eh_info;				qc = ata_qc_from_tag(ap, link->active_tag);				ata_ehi_clear_desc(ehi);				ata_ehi_push_desc(ehi, "irq_stat 0x%08x",						  irq_stat);			} else {				err_mask |= AC_ERR_HSM;				action |= ATA_EH_HARDRESET;				freeze = 1;			}		} else			qc = ata_qc_from_tag(ap, link->active_tag);		/* analyze CMD_ERR */		cerr = readl(port + PORT_CMD_ERR);		if (cerr < ARRAY_SIZE(sil24_cerr_db))			ci = &sil24_cerr_db[cerr];		if (ci && ci->desc) {			err_mask |= ci->err_mask;			action |= ci->action;			if (action & ATA_EH_RESET_MASK)				freeze = 1;			ata_ehi_push_desc(ehi, "%s", ci->desc);		} else {			err_mask |= AC_ERR_OTHER;			action |= ATA_EH_SOFTRESET;			freeze = 1;			ata_ehi_push_desc(ehi, "unknown command error %d",					  cerr);		}		/* record error info */		if (qc) {			sil24_read_tf(ap, qc->tag, &pp->tf);			qc->err_mask |= err_mask;		} else			ehi->err_mask |= err_mask;		ehi->action |= action;		/* if PMP, resume */		if (ap->nr_pmp_links)			writel(PORT_CS_PMP_RESUME, port + PORT_CTRL_STAT);	}	/* freeze or abort */	if (freeze)		ata_port_freeze(ap);	else if (abort) {		if (qc)			ata_link_abort(qc->dev->link);		else			ata_port_abort(ap);	}}static void sil24_finish_qc(struct ata_queued_cmd *qc){	struct ata_port *ap = qc->ap;	struct sil24_port_priv *pp = ap->private_data;	if (qc->flags & ATA_QCFLAG_RESULT_TF)		sil24_read_tf(ap, qc->tag, &pp->tf);}static inline void sil24_host_intr(struct ata_port *ap){	void __iomem *port = ap->ioaddr.cmd_addr;	u32 slot_stat, qc_active;	int rc;	/* If PCIX_IRQ_WOC, there's an inherent race window between	 * clearing IRQ pending status and reading PORT_SLOT_STAT	 * which may cause spurious interrupts afterwards.  This is	 * unavoidable and much better than losing interrupts which	 * happens if IRQ pending is cleared after reading	 * PORT_SLOT_STAT.	 */	if (ap->flags & SIL24_FLAG_PCIX_IRQ_WOC)		writel(PORT_IRQ_COMPLETE, port + PORT_IRQ_STAT);	slot_stat = readl(port + PORT_SLOT_STAT);	if (unlikely(slot_stat & HOST_SSTAT_ATTN)) {		sil24_error_intr(ap);		return;	}	qc_active = slot_stat & ~HOST_SSTAT_ATTN;	rc = ata_qc_complete_multiple(ap, qc_active, sil24_finish_qc);	if (rc > 0)		return;	if (rc < 0) {		struct ata_eh_info *ehi = &ap->link.eh_info;		ehi->err_mask |= AC_ERR_HSM;		ehi->action |= ATA_EH_SOFTRESET;		ata_port_freeze(ap);		return;	}	/* spurious interrupts are expected if PCIX_IRQ_WOC */	if (!(ap->flags & SIL24_FLAG_PCIX_IRQ_WOC) && ata_ratelimit())		ata_port_printk(ap, KERN_INFO, "spurious interrupt "			"(slot_stat 0x%x active_tag %d sactive 0x%x)\n",			slot_stat, ap->link.active_tag, ap->link.sactive);}static irqreturn_t sil24_interrupt(int irq, void *dev_instance){	struct ata_host *host = dev_instance;	void __iomem *host_base = host->iomap[SIL24_HOST_BAR];	unsigned handled = 0;	u32 status;	int i;	status = readl(host_base + HOST_IRQ_STAT);	if (status == 0xffffffff) {		printk(KERN_ERR DRV_NAME ": IRQ status == 0xffffffff, "		       "PCI fault or device removal?\n");		goto out;	}	if (!(status & IRQ_STAT_4PORTS))		goto out;	spin_lock(&host->lock);	for (i = 0; i < host->n_ports; i++)		if (status & (1 << i)) {			struct ata_port *ap = host->ports[i];			if (ap && !(ap->flags & ATA_FLAG_DISABLED)) {				sil24_host_intr(ap);				handled++;			} else				printk(KERN_ERR DRV_NAME				       ": interrupt from disabled port %d\n", i);		}	spin_unlock(&host->lock); out:	return IRQ_RETVAL(handled);}static void sil24_error_handler(struct ata_port *ap){	struct sil24_port_priv *pp = ap->private_data;	if (sil24_init_port(ap))		ata_eh_freeze_port(ap);	/* perform recovery */	sata_pmp_do_eh(ap, ata_std_prereset, sil24_softreset, sil24_hardreset,		       ata_std_postreset, sata_pmp_std_prereset,		       sil24_pmp_softreset, sil24_pmp_hardreset,		       sata_pmp_std_postreset);	pp->do_port_rst = 0;}static void sil24_post_internal_cmd(struct ata_queued_cmd *qc){	struct ata_port *ap = qc->ap;	/* make DMA engine forget about the failed command */	if ((qc->flags & ATA_QCFLAG_FAILED) && sil24_init_port(ap))		ata_eh_freeze_port(ap);}static int sil24_port_start(struct ata_port *ap){	struct device *dev = ap->host->dev;	struct sil24_port_priv *pp;	union sil24_cmd_block *cb;	size_t cb_size = sizeof(*cb) * SIL24_MAX_CMDS;	dma_addr_t cb_dma;	int rc;	pp = devm_kzalloc(dev, sizeof(*pp), GFP_KERNEL);	if (!pp)		return -ENOMEM;	pp->tf.command = ATA_DRDY;	cb = dmam_alloc_coherent(dev, cb_size, &cb_dma, GFP_KERNEL);	if (!cb)		return -ENOMEM;	memset(cb, 0, cb_size);	rc = ata_pad_alloc(ap, dev);	if (rc)		return rc;	pp->cmd_block = cb;	pp->cmd_block_dma = cb_dma;	ap->private_data = pp;	return 0;}static void sil24_init_controller(struct ata_host *host){	void __iomem *host_base = host->iomap[SIL24_HOST_BAR];	u32 tmp;	int i;	/* GPIO off */	writel(0, host_base + HOST_FLASH_CMD);	/* clear global reset & mask interrupts during initialization */	writel(0, host_base + HOST_CTRL);	/* init ports */	for (i = 0; i < host->n_ports; i++) {		struct ata_port *ap = host->ports[i];		void __iomem *port = ap->ioaddr.cmd_addr;		/* Initial PHY setting */		writel(0x20c, port + PORT_PHY_CFG);		/* Clear port RST */		tmp = readl(port + PORT_CTRL_STAT);		if (tmp & PORT_CS_PORT_RST) {			writel(PORT_CS_PORT_RST, port + PORT_CTRL_CLR);			tmp = ata_wait_register(port + PORT_CTRL_STAT,						PORT_CS_PORT_RST,						PORT_CS_PORT_RST, 10, 100);			if (tmp & PORT_CS_PORT_RST)				dev_printk(KERN_ERR, host->dev,					   "failed to clear port RST\n");		}		/* configure port */		sil24_config_port(ap);	}	/* Turn on interrupts */	writel(IRQ_STAT_4PORTS, host_base + HOST_CTRL);}static int sil24_init_one(struct pci_dev *pdev, const struct pci_device_id *ent){	extern int __MARKER__sil24_cmd_block_is_sized_wrongly;	static int printed_version;	struct ata_port_info pi = sil24_port_info[ent->driver_data];	const struct ata_port_info *ppi[] = { &pi, NULL };	void __iomem * const *iomap;	struct ata_host *host;	int i, rc;	u32 tmp;	/* cause link error if sil24_cmd_block is sized wrongly */	if (sizeof(union sil24_cmd_block) != PAGE_SIZE)		__MARKER__sil24_cmd_block_is_sized_wrongly = 1;	if (!printed_version++)		dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n");	/* acquire resources */	rc = pcim_enable_device(pdev);	if (rc)		return rc;	rc = pcim_iomap_regions(pdev,				(1 << SIL24_HOST_BAR) | (1 << SIL24_PORT_BAR),				DRV_NAME);	if (rc)		return rc;	iomap = pcim_iomap_table(pdev);	/* apply workaround for completion IRQ loss on PCI-X errata */	if (pi.flags & SIL24_FLAG_PCIX_IRQ_WOC) {		tmp = readl(iomap[SIL24_HOST_BAR] + HOST_CTRL);		if (tmp & (HOST_CTRL_TRDY | HOST_CTRL_STOP | HOST_CTRL_DEVSEL))			dev_printk(KERN_INFO, &pdev->dev,				   "Applying completion IRQ loss on PCI-X "				   "errata fix\n");		else			pi.flags &= ~SIL24_FLAG_PCIX_IRQ_WOC;	}	/* allocate and fill host */	host = ata_host_alloc_pinfo(&pdev->dev, ppi,				    SIL24_FLAG2NPORTS(ppi[0]->flags));	if (!host)		return -ENOMEM;	host->iomap = iomap;	for (i = 0; i < host->n_ports; i++) {		struct ata_port *ap = host->ports[i];		size_t offset = ap->port_no * PORT_REGS_SIZE;		void __iomem *port = iomap[SIL24_PORT_BAR] + offset;		host->ports[i]->ioaddr.cmd_addr = port;		host->ports[i]->ioaddr.scr_addr = port + PORT_SCONTROL;		ata_port_pbar_desc(ap, SIL24_HOST_BAR, -1, "host");		ata_port_pbar_desc(ap, SIL24_PORT_BAR, offset, "port");	}	/* configure and activate the device */	if (!pci_set_dma_mask(pdev, DMA_64BIT_MASK)) {		rc = pci_set_consistent_dma_mask(pdev, DMA_64BIT_MASK);		if (rc) {			rc = pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK);			if (rc) {				dev_printk(KERN_ERR, &pdev->dev,					   "64-bit DMA enable failed\n");				return rc;			}		}	} else {		rc = pci_set_dma_mask(pdev, DMA_32BIT_MASK);		if (rc) {			dev_printk(KERN_ERR, &pdev->dev,				   "32-bit DMA enable failed\n");			return rc;		}		rc = pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK);		if (rc) {			dev_printk(KERN_ERR, &pdev->dev,				   "32-bit consistent DMA enable failed\n");			return rc;		}	}	sil24_init_controller(host);	pci_set_master(pdev);	return ata_host_activate(host, pdev->irq, sil24_interrupt, IRQF_SHARED,				 &sil24_sht);}#ifdef CONFIG_PMstatic int sil24_pci_device_resume(struct pci_dev *pdev){	struct ata_host *host = dev_get_drvdata(&pdev->dev);	void __iomem *host_base = host->iomap[SIL24_HOST_BAR];	int rc;	rc = ata_pci_device_do_resume(pdev);	if (rc)		return rc;	if (pdev->dev.power.power_state.event == PM_EVENT_SUSPEND)		writel(HOST_CTRL_GLOBAL_RST, host_base + HOST_CTRL);	sil24_init_controller(host);	ata_host_resume(host);	return 0;}static int sil24_port_resume(struct ata_port *ap){	sil24_config_pmp(ap, ap->nr_pmp_links);	return 0;}#endifstatic int __init sil24_init(void){	return pci_register_driver(&sil24_pci_driver);}static void __exit sil24_exit(void){	pci_unregister_driver(&sil24_pci_driver);}MODULE_AUTHOR("Tejun Heo");MODULE_DESCRIPTION("Silicon Image 3124/3132 SATA low-level driver");MODULE_LICENSE("GPL");MODULE_DEVICE_TABLE(pci, sil24_pci_tbl);module_init(sil24_init);module_exit(sil24_exit);

⌨️ 快捷键说明

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