diff options
Diffstat (limited to 'drivers/ata/libahci.c')
| -rw-r--r-- | drivers/ata/libahci.c | 98 | 
1 files changed, 74 insertions, 24 deletions
diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c index acfd0f71106..d72ce047030 100644 --- a/drivers/ata/libahci.c +++ b/drivers/ata/libahci.c @@ -35,7 +35,6 @@  #include <linux/kernel.h>  #include <linux/gfp.h>  #include <linux/module.h> -#include <linux/init.h>  #include <linux/blkdev.h>  #include <linux/delay.h>  #include <linux/interrupt.h> @@ -69,7 +68,6 @@ static ssize_t ahci_transmit_led_message(struct ata_port *ap, u32 state,  static int ahci_scr_read(struct ata_link *link, unsigned int sc_reg, u32 *val);  static int ahci_scr_write(struct ata_link *link, unsigned int sc_reg, u32 val); -static unsigned int ahci_qc_issue(struct ata_queued_cmd *qc);  static bool ahci_qc_fill_rtf(struct ata_queued_cmd *qc);  static int ahci_port_start(struct ata_port *ap);  static void ahci_port_stop(struct ata_port *ap); @@ -89,7 +87,6 @@ static int ahci_pmp_retry_softreset(struct ata_link *link, unsigned int *class,  static int ahci_hardreset(struct ata_link *link, unsigned int *class,  			  unsigned long deadline);  static void ahci_postreset(struct ata_link *link, unsigned int *class); -static void ahci_error_handler(struct ata_port *ap);  static void ahci_post_internal_cmd(struct ata_queued_cmd *qc);  static void ahci_dev_config(struct ata_device *dev);  #ifdef CONFIG_PM @@ -189,14 +186,15 @@ struct ata_port_operations ahci_pmp_retry_srst_ops = {  };  EXPORT_SYMBOL_GPL(ahci_pmp_retry_srst_ops); -int ahci_em_messages = 1; +static bool ahci_em_messages __read_mostly = true;  EXPORT_SYMBOL_GPL(ahci_em_messages); -module_param(ahci_em_messages, int, 0444); +module_param(ahci_em_messages, bool, 0444);  /* add other LED protocol types when they become supported */  MODULE_PARM_DESC(ahci_em_messages,  	"AHCI Enclosure Management Message control (0 = off, 1 = on)"); -int devslp_idle_timeout = 1000;	/* device sleep idle timeout in ms */ +/* device sleep idle timeout in ms */ +static int devslp_idle_timeout __read_mostly = 1000;  module_param(devslp_idle_timeout, int, 0644);  MODULE_PARM_DESC(devslp_idle_timeout, "device sleep idle timeout"); @@ -394,6 +392,9 @@ static ssize_t ahci_show_em_supported(struct device *dev,   *   *	If inconsistent, config values are fixed up by this function.   * + *	If it is not set already this function sets hpriv->start_engine to + *	ahci_start_engine. + *   *	LOCKING:   *	None.   */ @@ -450,11 +451,23 @@ void ahci_save_initial_config(struct device *dev,  		cap &= ~HOST_CAP_SNTF;  	} +	if ((cap2 & HOST_CAP2_SDS) && (hpriv->flags & AHCI_HFLAG_NO_DEVSLP)) { +		dev_info(dev, +			 "controller can't do DEVSLP, turning off\n"); +		cap2 &= ~HOST_CAP2_SDS; +		cap2 &= ~HOST_CAP2_SADM; +	} +  	if (!(cap & HOST_CAP_FBS) && (hpriv->flags & AHCI_HFLAG_YES_FBS)) {  		dev_info(dev, "controller can do FBS, turning on CAP_FBS\n");  		cap |= HOST_CAP_FBS;  	} +	if ((cap & HOST_CAP_FBS) && (hpriv->flags & AHCI_HFLAG_NO_FBS)) { +		dev_info(dev, "controller can't do FBS, turning off CAP_FBS\n"); +		cap &= ~HOST_CAP_FBS; +	} +  	if (force_port_map && port_map != force_port_map) {  		dev_info(dev, "forcing port_map 0x%x -> 0x%x\n",  			 port_map, force_port_map); @@ -500,6 +513,9 @@ void ahci_save_initial_config(struct device *dev,  	hpriv->cap = cap;  	hpriv->cap2 = cap2;  	hpriv->port_map = port_map; + +	if (!hpriv->start_engine) +		hpriv->start_engine = ahci_start_engine;  }  EXPORT_SYMBOL_GPL(ahci_save_initial_config); @@ -603,7 +619,7 @@ int ahci_stop_engine(struct ata_port *ap)  }  EXPORT_SYMBOL_GPL(ahci_stop_engine); -static void ahci_start_fis_rx(struct ata_port *ap) +void ahci_start_fis_rx(struct ata_port *ap)  {  	void __iomem *port_mmio = ahci_port_base(ap);  	struct ahci_host_priv *hpriv = ap->host->private_data; @@ -629,6 +645,7 @@ static void ahci_start_fis_rx(struct ata_port *ap)  	/* flush */  	readl(port_mmio + PORT_CMD);  } +EXPORT_SYMBOL_GPL(ahci_start_fis_rx);  static int ahci_stop_fis_rx(struct ata_port *ap)  { @@ -766,7 +783,7 @@ static void ahci_start_port(struct ata_port *ap)  	/* enable DMA */  	if (!(hpriv->flags & AHCI_HFLAG_DELAY_ENGINE)) -		ahci_start_engine(ap); +		hpriv->start_engine(ap);  	/* turn on LEDs */  	if (ap->flags & ATA_FLAG_EM) { @@ -778,8 +795,16 @@ static void ahci_start_port(struct ata_port *ap)  				rc = ap->ops->transmit_led_message(ap,  							       emp->led_state,  							       4); +				/* +				 * If busy, give a breather but do not +				 * release EH ownership by using msleep() +				 * instead of ata_msleep().  EM Transmit +				 * bit is busy for the whole host and +				 * releasing ownership will cause other +				 * ports to fail the same way. +				 */  				if (rc == -EBUSY) -					ata_msleep(ap, 1); +					msleep(1);  				else  					break;  			} @@ -1024,12 +1049,13 @@ static ssize_t ahci_led_show(struct ata_port *ap, char *buf)  static ssize_t ahci_led_store(struct ata_port *ap, const char *buf,  				size_t size)  { -	int state; +	unsigned int state;  	int pmp;  	struct ahci_port_priv *pp = ap->private_data;  	struct ahci_em_priv *emp; -	state = simple_strtoul(buf, NULL, 0); +	if (kstrtouint(buf, 0, &state) < 0) +		return -EINVAL;  	/* get the slot number from the message */  	pmp = (state & EM_MSG_LED_PMP_SLOT) >> 8; @@ -1226,7 +1252,7 @@ int ahci_kick_engine(struct ata_port *ap)  	/* restart engine */   out_restart: -	ahci_start_engine(ap); +	hpriv->start_engine(ap);  	return rc;  }  EXPORT_SYMBOL_GPL(ahci_kick_engine); @@ -1267,9 +1293,11 @@ int ahci_do_softreset(struct ata_link *link, unsigned int *class,  {  	struct ata_port *ap = link->ap;  	struct ahci_host_priv *hpriv = ap->host->private_data; +	struct ahci_port_priv *pp = ap->private_data;  	const char *reason = NULL;  	unsigned long now, msecs;  	struct ata_taskfile tf; +	bool fbs_disabled = false;  	int rc;  	DPRINTK("ENTER\n"); @@ -1279,6 +1307,16 @@ int ahci_do_softreset(struct ata_link *link, unsigned int *class,  	if (rc && rc != -EOPNOTSUPP)  		ata_link_warn(link, "failed to reset engine (errno=%d)\n", rc); +	/* +	 * According to AHCI-1.2 9.3.9: if FBS is enable, software shall +	 * clear PxFBS.EN to '0' prior to issuing software reset to devices +	 * that is attached to port multiplier. +	 */ +	if (!ata_is_host_link(link) && pp->fbs_enabled) { +		ahci_disable_fbs(ap); +		fbs_disabled = true; +	} +  	ata_tf_init(link->device, &tf);  	/* issue the first D2H Register FIS */ @@ -1319,6 +1357,10 @@ int ahci_do_softreset(struct ata_link *link, unsigned int *class,  	} else  		*class = ahci_dev_classify(ap); +	/* re-enable FBS if disabled before */ +	if (fbs_disabled) +		ahci_enable_fbs(ap); +  	DPRINTK("EXIT, class=%u\n", *class);  	return 0; @@ -1363,8 +1405,8 @@ static int ahci_bad_pmp_check_ready(struct ata_link *link)  	return ata_check_ready(status);  } -int ahci_pmp_retry_softreset(struct ata_link *link, unsigned int *class, -				unsigned long deadline) +static int ahci_pmp_retry_softreset(struct ata_link *link, unsigned int *class, +				    unsigned long deadline)  {  	struct ata_port *ap = link->ap;  	void __iomem *port_mmio = ahci_port_base(ap); @@ -1402,6 +1444,7 @@ static int ahci_hardreset(struct ata_link *link, unsigned int *class,  	const unsigned long *timing = sata_ehc_deb_timing(&link->eh_context);  	struct ata_port *ap = link->ap;  	struct ahci_port_priv *pp = ap->private_data; +	struct ahci_host_priv *hpriv = ap->host->private_data;  	u8 *d2h_fis = pp->rx_fis + RX_FIS_D2H_REG;  	struct ata_taskfile tf;  	bool online; @@ -1419,7 +1462,7 @@ static int ahci_hardreset(struct ata_link *link, unsigned int *class,  	rc = sata_link_hardreset(link, timing, deadline, &online,  				 ahci_check_ready); -	ahci_start_engine(ap); +	hpriv->start_engine(ap);  	if (online)  		*class = ahci_dev_classify(ap); @@ -1605,7 +1648,7 @@ static void ahci_error_intr(struct ata_port *ap, u32 irq_stat)  	}  	if (irq_stat & PORT_IRQ_UNK_FIS) { -		u32 *unk = (u32 *)(pp->rx_fis + RX_FIS_UNK); +		u32 *unk = pp->rx_fis + RX_FIS_UNK;  		active_ehi->err_mask |= AC_ERR_HSM;  		active_ehi->action |= ATA_EH_RESET; @@ -1740,7 +1783,7 @@ static void ahci_handle_port_interrupt(struct ata_port *ap,  	}  } -void ahci_port_intr(struct ata_port *ap) +static void ahci_port_intr(struct ata_port *ap)  {  	void __iomem *port_mmio = ahci_port_base(ap);  	u32 status; @@ -1773,7 +1816,7 @@ irqreturn_t ahci_thread_fn(int irq, void *dev_instance)  }  EXPORT_SYMBOL_GPL(ahci_thread_fn); -void ahci_hw_port_interrupt(struct ata_port *ap) +static void ahci_hw_port_interrupt(struct ata_port *ap)  {  	void __iomem *port_mmio = ahci_port_base(ap);  	struct ahci_port_priv *pp = ap->private_data; @@ -1902,7 +1945,7 @@ irqreturn_t ahci_interrupt(int irq, void *dev_instance)  }  EXPORT_SYMBOL_GPL(ahci_interrupt); -static unsigned int ahci_qc_issue(struct ata_queued_cmd *qc) +unsigned int ahci_qc_issue(struct ata_queued_cmd *qc)  {  	struct ata_port *ap = qc->ap;  	void __iomem *port_mmio = ahci_port_base(ap); @@ -1931,6 +1974,7 @@ static unsigned int ahci_qc_issue(struct ata_queued_cmd *qc)  	return 0;  } +EXPORT_SYMBOL_GPL(ahci_qc_issue);  static bool ahci_qc_fill_rtf(struct ata_queued_cmd *qc)  { @@ -1981,12 +2025,14 @@ static void ahci_thaw(struct ata_port *ap)  	writel(pp->intr_mask, port_mmio + PORT_IRQ_MASK);  } -static void ahci_error_handler(struct ata_port *ap) +void ahci_error_handler(struct ata_port *ap)  { +	struct ahci_host_priv *hpriv = ap->host->private_data; +  	if (!(ap->pflags & ATA_PFLAG_FROZEN)) {  		/* restart engine */  		ahci_stop_engine(ap); -		ahci_start_engine(ap); +		hpriv->start_engine(ap);  	}  	sata_pmp_error_handler(ap); @@ -1994,6 +2040,7 @@ static void ahci_error_handler(struct ata_port *ap)  	if (!ata_dev_enabled(ap->link.device))  		ahci_stop_engine(ap);  } +EXPORT_SYMBOL_GPL(ahci_error_handler);  static void ahci_post_internal_cmd(struct ata_queued_cmd *qc)  { @@ -2006,6 +2053,7 @@ static void ahci_post_internal_cmd(struct ata_queued_cmd *qc)  static void ahci_set_aggressive_devslp(struct ata_port *ap, bool sleep)  { +	struct ahci_host_priv *hpriv = ap->host->private_data;  	void __iomem *port_mmio = ahci_port_base(ap);  	struct ata_device *dev = ap->link.device;  	u32 devslp, dm, dito, mdat, deto; @@ -2069,7 +2117,7 @@ static void ahci_set_aggressive_devslp(struct ata_port *ap, bool sleep)  		   PORT_DEVSLP_ADSE);  	writel(devslp, port_mmio + PORT_DEVSLP); -	ahci_start_engine(ap); +	hpriv->start_engine(ap);  	/* enable device sleep feature for the drive */  	err_mask = ata_dev_set_feature(dev, @@ -2081,6 +2129,7 @@ static void ahci_set_aggressive_devslp(struct ata_port *ap, bool sleep)  static void ahci_enable_fbs(struct ata_port *ap)  { +	struct ahci_host_priv *hpriv = ap->host->private_data;  	struct ahci_port_priv *pp = ap->private_data;  	void __iomem *port_mmio = ahci_port_base(ap);  	u32 fbs; @@ -2109,11 +2158,12 @@ static void ahci_enable_fbs(struct ata_port *ap)  	} else  		dev_err(ap->host->dev, "Failed to enable FBS\n"); -	ahci_start_engine(ap); +	hpriv->start_engine(ap);  }  static void ahci_disable_fbs(struct ata_port *ap)  { +	struct ahci_host_priv *hpriv = ap->host->private_data;  	struct ahci_port_priv *pp = ap->private_data;  	void __iomem *port_mmio = ahci_port_base(ap);  	u32 fbs; @@ -2141,7 +2191,7 @@ static void ahci_disable_fbs(struct ata_port *ap)  		pp->fbs_enabled = false;  	} -	ahci_start_engine(ap); +	hpriv->start_engine(ap);  }  static void ahci_pmp_attach(struct ata_port *ap)  | 
