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

📄 libata-pmp.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * libata-pmp.c - libata port multiplier support * * Copyright (c) 2007  SUSE Linux Products GmbH * Copyright (c) 2007  Tejun Heo <teheo@suse.de> * * This file is released under the GPLv2. */#include <linux/kernel.h>#include <linux/libata.h>#include "libata.h"/** *	sata_pmp_read - read PMP register *	@link: link to read PMP register for *	@reg: register to read *	@r_val: resulting value * *	Read PMP register. * *	LOCKING: *	Kernel thread context (may sleep). * *	RETURNS: *	0 on success, AC_ERR_* mask on failure. */static unsigned int sata_pmp_read(struct ata_link *link, int reg, u32 *r_val){	struct ata_port *ap = link->ap;	struct ata_device *pmp_dev = ap->link.device;	struct ata_taskfile tf;	unsigned int err_mask;	ata_tf_init(pmp_dev, &tf);	tf.command = ATA_CMD_PMP_READ;	tf.protocol = ATA_PROT_NODATA;	tf.flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE;	tf.feature = reg;	tf.device = link->pmp;	err_mask = ata_exec_internal(pmp_dev, &tf, NULL, DMA_NONE, NULL, 0,				     SATA_PMP_SCR_TIMEOUT);	if (err_mask)		return err_mask;	*r_val = tf.nsect | tf.lbal << 8 | tf.lbam << 16 | tf.lbah << 24;	return 0;}/** *	sata_pmp_write - write PMP register *	@link: link to write PMP register for *	@reg: register to write *	@r_val: value to write * *	Write PMP register. * *	LOCKING: *	Kernel thread context (may sleep). * *	RETURNS: *	0 on success, AC_ERR_* mask on failure. */static unsigned int sata_pmp_write(struct ata_link *link, int reg, u32 val){	struct ata_port *ap = link->ap;	struct ata_device *pmp_dev = ap->link.device;	struct ata_taskfile tf;	ata_tf_init(pmp_dev, &tf);	tf.command = ATA_CMD_PMP_WRITE;	tf.protocol = ATA_PROT_NODATA;	tf.flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE;	tf.feature = reg;	tf.device = link->pmp;	tf.nsect = val & 0xff;	tf.lbal = (val >> 8) & 0xff;	tf.lbam = (val >> 16) & 0xff;	tf.lbah = (val >> 24) & 0xff;	return ata_exec_internal(pmp_dev, &tf, NULL, DMA_NONE, NULL, 0,				 SATA_PMP_SCR_TIMEOUT);}/** *	sata_pmp_qc_defer_cmd_switch - qc_defer for command switching PMP *	@qc: ATA command in question * *	A host which has command switching PMP support cannot issue *	commands to multiple links simultaneously. * *	LOCKING: *	spin_lock_irqsave(host lock) * *	RETURNS: *	ATA_DEFER_* if deferring is needed, 0 otherwise. */int sata_pmp_qc_defer_cmd_switch(struct ata_queued_cmd *qc){	struct ata_link *link = qc->dev->link;	struct ata_port *ap = link->ap;	if (ap->excl_link == NULL || ap->excl_link == link) {		if (ap->nr_active_links == 0 || ata_link_active(link)) {			qc->flags |= ATA_QCFLAG_CLEAR_EXCL;			return ata_std_qc_defer(qc);		}		ap->excl_link = link;	}	return ATA_DEFER_PORT;}/** *	sata_pmp_scr_read - read PSCR *	@link: ATA link to read PSCR for *	@reg: PSCR to read *	@r_val: resulting value * *	Read PSCR @reg into @r_val for @link, to be called from *	ata_scr_read(). * *	LOCKING: *	Kernel thread context (may sleep). * *	RETURNS: *	0 on success, -errno on failure. */int sata_pmp_scr_read(struct ata_link *link, int reg, u32 *r_val){	unsigned int err_mask;	if (reg > SATA_PMP_PSCR_CONTROL)		return -EINVAL;	err_mask = sata_pmp_read(link, reg, r_val);	if (err_mask) {		ata_link_printk(link, KERN_WARNING, "failed to read SCR %d "				"(Emask=0x%x)\n", reg, err_mask);		return -EIO;	}	return 0;}/** *	sata_pmp_scr_write - write PSCR *	@link: ATA link to write PSCR for *	@reg: PSCR to write *	@val: value to be written * *	Write @val to PSCR @reg for @link, to be called from *	ata_scr_write() and ata_scr_write_flush(). * *	LOCKING: *	Kernel thread context (may sleep). * *	RETURNS: *	0 on success, -errno on failure. */int sata_pmp_scr_write(struct ata_link *link, int reg, u32 val){	unsigned int err_mask;	if (reg > SATA_PMP_PSCR_CONTROL)		return -EINVAL;	err_mask = sata_pmp_write(link, reg, val);	if (err_mask) {		ata_link_printk(link, KERN_WARNING, "failed to write SCR %d "				"(Emask=0x%x)\n", reg, err_mask);		return -EIO;	}	return 0;}/** *	sata_pmp_std_prereset - prepare PMP link for reset *	@link: link to be reset *	@deadline: deadline jiffies for the operation * *	@link is about to be reset.  Initialize it. * *	LOCKING: *	Kernel thread context (may sleep) * *	RETURNS: *	0 on success, -errno otherwise. */int sata_pmp_std_prereset(struct ata_link *link, unsigned long deadline){	struct ata_eh_context *ehc = &link->eh_context;	const unsigned long *timing = sata_ehc_deb_timing(ehc);	int rc;	/* force HRST? */	if (link->flags & ATA_LFLAG_NO_SRST)		ehc->i.action |= ATA_EH_HARDRESET;	/* handle link resume */	if ((ehc->i.flags & ATA_EHI_RESUME_LINK) &&	    (link->flags & ATA_LFLAG_HRST_TO_RESUME))		ehc->i.action |= ATA_EH_HARDRESET;	/* if we're about to do hardreset, nothing more to do */	if (ehc->i.action & ATA_EH_HARDRESET)		return 0;	/* resume link */	rc = sata_link_resume(link, timing, deadline);	if (rc) {		/* phy resume failed */		ata_link_printk(link, KERN_WARNING, "failed to resume link "				"for reset (errno=%d)\n", rc);		return rc;	}	/* clear SError bits including .X which blocks the port when set */	rc = sata_scr_write(link, SCR_ERROR, 0xffffffff);	if (rc) {		ata_link_printk(link, KERN_ERR,				"failed to clear SError (errno=%d)\n", rc);		return rc;	}	return 0;}/** *	sata_pmp_std_hardreset - standard hardreset method for PMP link *	@link: link to be reset *	@class: resulting class of attached device *	@deadline: deadline jiffies for the operation * *	Hardreset PMP port @link.  Note that this function doesn't *	wait for BSY clearance.  There simply isn't a generic way to *	wait the event.  Instead, this function return -EAGAIN thus *	telling libata-EH to followup with softreset. * *	LOCKING: *	Kernel thread context (may sleep) * *	RETURNS: *	0 on success, -errno otherwise. */int sata_pmp_std_hardreset(struct ata_link *link, unsigned int *class,			   unsigned long deadline){	const unsigned long *timing = sata_ehc_deb_timing(&link->eh_context);	u32 tmp;	int rc;	DPRINTK("ENTER\n");	/* do hardreset */	rc = sata_link_hardreset(link, timing, deadline);	if (rc) {		ata_link_printk(link, KERN_ERR,				"COMRESET failed (errno=%d)\n", rc);		goto out;	}	/* clear SError bits including .X which blocks the port when set */	rc = sata_scr_write(link, SCR_ERROR, 0xffffffff);	if (rc) {		ata_link_printk(link, KERN_ERR, "failed to clear SError "				"during hardreset (errno=%d)\n", rc);		goto out;	}	/* if device is present, follow up with srst to wait for !BSY */	if (ata_link_online(link))		rc = -EAGAIN; out:	/* if SCR isn't accessible, we need to reset the PMP */	if (rc && rc != -EAGAIN && sata_scr_read(link, SCR_STATUS, &tmp))		rc = -ERESTART;	DPRINTK("EXIT, rc=%d\n", rc);	return rc;}/** *	ata_std_postreset - standard postreset method for PMP link *	@link: the target ata_link *	@classes: classes of attached devices * *	This function is invoked after a successful reset.  Note that *	the device might have been reset more than once using *	different reset methods before postreset is invoked. * *	LOCKING: *	Kernel thread context (may sleep) */void sata_pmp_std_postreset(struct ata_link *link, unsigned int *class){	u32 serror;	DPRINTK("ENTER\n");	/* clear SError */	if (sata_scr_read(link, SCR_ERROR, &serror) == 0)		sata_scr_write(link, SCR_ERROR, serror);	/* print link status */	sata_print_link_status(link);	DPRINTK("EXIT\n");}/** *	sata_pmp_read_gscr - read GSCR block of SATA PMP *	@dev: PMP device *	@gscr: buffer to read GSCR block into * *	Read selected PMP GSCRs from the PMP at @dev.  This will serve *	as configuration and identification info for the PMP. * *	LOCKING: *	Kernel thread context (may sleep). * *	RETURNS: *	0 on success, -errno on failure. */static int sata_pmp_read_gscr(struct ata_device *dev, u32 *gscr){	static const int gscr_to_read[] = { 0, 1, 2, 32, 33, 64, 96 };	int i;	for (i = 0; i < ARRAY_SIZE(gscr_to_read); i++) {		int reg = gscr_to_read[i];		unsigned int err_mask;		err_mask = sata_pmp_read(dev->link, reg, &gscr[reg]);		if (err_mask) {			ata_dev_printk(dev, KERN_ERR, "failed to read PMP "				"GSCR[%d] (Emask=0x%x)\n", reg, err_mask);			return -EIO;		}	}	return 0;}static const char *sata_pmp_spec_rev_str(const u32 *gscr){	u32 rev = gscr[SATA_PMP_GSCR_REV];	if (rev & (1 << 2))		return "1.1";	if (rev & (1 << 1))		return "1.0";	return "<unknown>";}static int sata_pmp_configure(struct ata_device *dev, int print_info){	struct ata_port *ap = dev->link->ap;	u32 *gscr = dev->gscr;	unsigned int err_mask = 0;	const char *reason;	int nr_ports, rc;	nr_ports = sata_pmp_gscr_ports(gscr);	if (nr_ports <= 0 || nr_ports > SATA_PMP_MAX_PORTS) {		rc = -EINVAL;		reason = "invalid nr_ports";		goto fail;	}	if ((ap->flags & ATA_FLAG_AN) &&	    (gscr[SATA_PMP_GSCR_FEAT] & SATA_PMP_FEAT_NOTIFY))		dev->flags |= ATA_DFLAG_AN;	/* monitor SERR_PHYRDY_CHG on fan-out ports */	err_mask = sata_pmp_write(dev->link, SATA_PMP_GSCR_ERROR_EN,				  SERR_PHYRDY_CHG);	if (err_mask) {		rc = -EIO;		reason = "failed to write GSCR_ERROR_EN";		goto fail;	}	/* turn off notification till fan-out ports are reset and configured */	if (gscr[SATA_PMP_GSCR_FEAT_EN] & SATA_PMP_FEAT_NOTIFY) {		gscr[SATA_PMP_GSCR_FEAT_EN] &= ~SATA_PMP_FEAT_NOTIFY;		err_mask = sata_pmp_write(dev->link, SATA_PMP_GSCR_FEAT_EN,					  gscr[SATA_PMP_GSCR_FEAT_EN]);		if (err_mask) {			rc = -EIO;			reason = "failed to write GSCR_FEAT_EN";			goto fail;		}	}	if (print_info) {		ata_dev_printk(dev, KERN_INFO, "Port Multiplier %s, "			       "0x%04x:0x%04x r%d, %d ports, feat 0x%x/0x%x\n",			       sata_pmp_spec_rev_str(gscr),			       sata_pmp_gscr_vendor(gscr),			       sata_pmp_gscr_devid(gscr),			       sata_pmp_gscr_rev(gscr),			       nr_ports, gscr[SATA_PMP_GSCR_FEAT_EN],			       gscr[SATA_PMP_GSCR_FEAT]);		if (!(dev->flags & ATA_DFLAG_AN))			ata_dev_printk(dev, KERN_INFO,				"Asynchronous notification not supported, "				"hotplug won't\n         work on fan-out "				"ports. Use warm-plug instead.\n");	}	return 0; fail:	ata_dev_printk(dev, KERN_ERR,		       "failed to configure Port Multiplier (%s, Emask=0x%x)\n",		       reason, err_mask);	return rc;}static int sata_pmp_init_links(struct ata_port *ap, int nr_ports){	struct ata_link *pmp_link = ap->pmp_link;	int i;	if (!pmp_link) {		pmp_link = kzalloc(sizeof(pmp_link[0]) * SATA_PMP_MAX_PORTS,				   GFP_NOIO);		if (!pmp_link)			return -ENOMEM;		for (i = 0; i < SATA_PMP_MAX_PORTS; i++)			ata_link_init(ap, &pmp_link[i], i);		ap->pmp_link = pmp_link;	}	for (i = 0; i < nr_ports; i++) {		struct ata_link *link = &pmp_link[i];		struct ata_eh_context *ehc = &link->eh_context;		link->flags = 0;		ehc->i.probe_mask |= 1;		ehc->i.action |= ATA_EH_SOFTRESET;		ehc->i.flags |= ATA_EHI_RESUME_LINK;	}	return 0;}static void sata_pmp_quirks(struct ata_port *ap){	u32 *gscr = ap->link.device->gscr;	u16 vendor = sata_pmp_gscr_vendor(gscr);	u16 devid = sata_pmp_gscr_devid(gscr);	struct ata_link *link;	if (vendor == 0x1095 && devid == 0x3726) {		/* sil3726 quirks */		ata_port_for_each_link(link, ap) {			/* SError.N need a kick in the ass to get working */			link->flags |= ATA_LFLAG_HRST_TO_RESUME;			/* class code report is unreliable */			if (link->pmp < 5)				link->flags |= ATA_LFLAG_ASSUME_ATA;			/* port 5 is for SEMB device and it doesn't like SRST */			if (link->pmp == 5)				link->flags |= ATA_LFLAG_NO_SRST |					       ATA_LFLAG_ASSUME_SEMB;		}	} else if (vendor == 0x1095 && devid == 0x4723) {		/* sil4723 quirks */		ata_port_for_each_link(link, ap) {			/* SError.N need a kick in the ass to get working */			link->flags |= ATA_LFLAG_HRST_TO_RESUME;			/* class code report is unreliable */			if (link->pmp < 2)				link->flags |= ATA_LFLAG_ASSUME_ATA;			/* the config device at port 2 locks up on SRST */			if (link->pmp == 2)				link->flags |= ATA_LFLAG_NO_SRST |					       ATA_LFLAG_ASSUME_ATA;		}	} else if (vendor == 0x1095 && devid == 0x4726) {		/* sil4726 quirks */		ata_port_for_each_link(link, ap) {			/* SError.N need a kick in the ass to get working */			link->flags |= ATA_LFLAG_HRST_TO_RESUME;			/* Class code report is unreliable and SRST			 * times out under certain configurations.			 * Config device can be at port 0 or 5 and			 * locks up on SRST.			 */			if (link->pmp <= 5)				link->flags |= ATA_LFLAG_NO_SRST |					       ATA_LFLAG_ASSUME_ATA;			/* Port 6 is for SEMB device which doesn't			 * like SRST either.			 */			if (link->pmp == 6)				link->flags |= ATA_LFLAG_NO_SRST |					       ATA_LFLAG_ASSUME_SEMB;		}	} else if (vendor == 0x1095 && (devid == 0x5723 || devid == 0x5733 ||					devid == 0x5734 || devid == 0x5744)) {		/* sil5723/5744 quirks */		/* sil5723/5744 has either two or three downstream		 * ports depending on operation mode.  The last port		 * is empty if any actual IO device is available or		 * occupied by a pseudo configuration device		 * otherwise.  Don't try hard to recover it.		 */		ap->pmp_link[ap->nr_pmp_links - 1].flags |= ATA_LFLAG_NO_RETRY;	} else if (vendor == 0x11ab && devid == 0x4140) {		/* Marvell 88SM4140 quirks.  Fan-out ports require PHY		 * reset to work; other than that, it behaves very		 * nicely.		 */		ata_port_for_each_link(link, ap)			link->flags |= ATA_LFLAG_HRST_TO_RESUME;	}}/** *	sata_pmp_attach - attach a SATA PMP device *	@dev: SATA PMP device to attach * *	Configure and attach SATA PMP device @dev.  This function is *	also responsible for allocating and initializing PMP links. * *	LOCKING: *	Kernel thread context (may sleep). * *	RETURNS: *	0 on success, -errno on failure. */int sata_pmp_attach(struct ata_device *dev){	struct ata_link *link = dev->link;	struct ata_port *ap = link->ap;	unsigned long flags;	struct ata_link *tlink;	int rc;	/* is it hanging off the right place? */	if (!(ap->flags & ATA_FLAG_PMP)) {		ata_dev_printk(dev, KERN_ERR,			       "host does not support Port Multiplier\n");		return -EINVAL;	}	if (!ata_is_host_link(link)) {		ata_dev_printk(dev, KERN_ERR,			       "Port Multipliers cannot be nested\n");		return -EINVAL;	}	if (dev->devno) {		ata_dev_printk(dev, KERN_ERR,			       "Port Multiplier must be the first device\n");		return -EINVAL;	}	WARN_ON(link->pmp != 0);	link->pmp = SATA_PMP_CTRL_PORT;	/* read GSCR block */	rc = sata_pmp_read_gscr(dev, dev->gscr);	if (rc)		goto fail;	/* config PMP */	rc = sata_pmp_configure(dev, 1);	if (rc)		goto fail;	rc = sata_pmp_init_links(ap, sata_pmp_gscr_ports(dev->gscr));	if (rc) {		ata_dev_printk(dev, KERN_INFO,			       "failed to initialize PMP links\n");		goto fail;	}	/* attach it */

⌨️ 快捷键说明

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