18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  libahci.c - Common AHCI SATA low-level routines
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *  Maintained by:  Tejun Heo <tj@kernel.org>
68c2ecf20Sopenharmony_ci *    		    Please ALWAYS copy linux-ide@vger.kernel.org
78c2ecf20Sopenharmony_ci *		    on emails.
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci *  Copyright 2004-2005 Red Hat, Inc.
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci * libata documentation is available via 'make {ps|pdf}docs',
128c2ecf20Sopenharmony_ci * as Documentation/driver-api/libata.rst
138c2ecf20Sopenharmony_ci *
148c2ecf20Sopenharmony_ci * AHCI hardware documentation:
158c2ecf20Sopenharmony_ci * http://www.intel.com/technology/serialata/pdf/rev1_0.pdf
168c2ecf20Sopenharmony_ci * http://www.intel.com/technology/serialata/pdf/rev1_1.pdf
178c2ecf20Sopenharmony_ci */
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#include <linux/kernel.h>
208c2ecf20Sopenharmony_ci#include <linux/gfp.h>
218c2ecf20Sopenharmony_ci#include <linux/module.h>
228c2ecf20Sopenharmony_ci#include <linux/nospec.h>
238c2ecf20Sopenharmony_ci#include <linux/blkdev.h>
248c2ecf20Sopenharmony_ci#include <linux/delay.h>
258c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
268c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h>
278c2ecf20Sopenharmony_ci#include <linux/device.h>
288c2ecf20Sopenharmony_ci#include <scsi/scsi_host.h>
298c2ecf20Sopenharmony_ci#include <scsi/scsi_cmnd.h>
308c2ecf20Sopenharmony_ci#include <linux/libata.h>
318c2ecf20Sopenharmony_ci#include <linux/pci.h>
328c2ecf20Sopenharmony_ci#include "ahci.h"
338c2ecf20Sopenharmony_ci#include "libata.h"
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_cistatic int ahci_skip_host_reset;
368c2ecf20Sopenharmony_ciint ahci_ignore_sss;
378c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ahci_ignore_sss);
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_cimodule_param_named(skip_host_reset, ahci_skip_host_reset, int, 0444);
408c2ecf20Sopenharmony_ciMODULE_PARM_DESC(skip_host_reset, "skip global host reset (0=don't skip, 1=skip)");
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_cimodule_param_named(ignore_sss, ahci_ignore_sss, int, 0444);
438c2ecf20Sopenharmony_ciMODULE_PARM_DESC(ignore_sss, "Ignore staggered spinup flag (0=don't ignore, 1=ignore)");
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_cistatic int ahci_set_lpm(struct ata_link *link, enum ata_lpm_policy policy,
468c2ecf20Sopenharmony_ci			unsigned hints);
478c2ecf20Sopenharmony_cistatic ssize_t ahci_led_show(struct ata_port *ap, char *buf);
488c2ecf20Sopenharmony_cistatic ssize_t ahci_led_store(struct ata_port *ap, const char *buf,
498c2ecf20Sopenharmony_ci			      size_t size);
508c2ecf20Sopenharmony_cistatic ssize_t ahci_transmit_led_message(struct ata_port *ap, u32 state,
518c2ecf20Sopenharmony_ci					ssize_t size);
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_cistatic int ahci_scr_read(struct ata_link *link, unsigned int sc_reg, u32 *val);
568c2ecf20Sopenharmony_cistatic int ahci_scr_write(struct ata_link *link, unsigned int sc_reg, u32 val);
578c2ecf20Sopenharmony_cistatic bool ahci_qc_fill_rtf(struct ata_queued_cmd *qc);
588c2ecf20Sopenharmony_cistatic int ahci_port_start(struct ata_port *ap);
598c2ecf20Sopenharmony_cistatic void ahci_port_stop(struct ata_port *ap);
608c2ecf20Sopenharmony_cistatic enum ata_completion_errors ahci_qc_prep(struct ata_queued_cmd *qc);
618c2ecf20Sopenharmony_cistatic int ahci_pmp_qc_defer(struct ata_queued_cmd *qc);
628c2ecf20Sopenharmony_cistatic void ahci_freeze(struct ata_port *ap);
638c2ecf20Sopenharmony_cistatic void ahci_thaw(struct ata_port *ap);
648c2ecf20Sopenharmony_cistatic void ahci_set_aggressive_devslp(struct ata_port *ap, bool sleep);
658c2ecf20Sopenharmony_cistatic void ahci_enable_fbs(struct ata_port *ap);
668c2ecf20Sopenharmony_cistatic void ahci_disable_fbs(struct ata_port *ap);
678c2ecf20Sopenharmony_cistatic void ahci_pmp_attach(struct ata_port *ap);
688c2ecf20Sopenharmony_cistatic void ahci_pmp_detach(struct ata_port *ap);
698c2ecf20Sopenharmony_cistatic int ahci_softreset(struct ata_link *link, unsigned int *class,
708c2ecf20Sopenharmony_ci			  unsigned long deadline);
718c2ecf20Sopenharmony_cistatic int ahci_pmp_retry_softreset(struct ata_link *link, unsigned int *class,
728c2ecf20Sopenharmony_ci			  unsigned long deadline);
738c2ecf20Sopenharmony_cistatic int ahci_hardreset(struct ata_link *link, unsigned int *class,
748c2ecf20Sopenharmony_ci			  unsigned long deadline);
758c2ecf20Sopenharmony_cistatic void ahci_postreset(struct ata_link *link, unsigned int *class);
768c2ecf20Sopenharmony_cistatic void ahci_post_internal_cmd(struct ata_queued_cmd *qc);
778c2ecf20Sopenharmony_cistatic void ahci_dev_config(struct ata_device *dev);
788c2ecf20Sopenharmony_ci#ifdef CONFIG_PM
798c2ecf20Sopenharmony_cistatic int ahci_port_suspend(struct ata_port *ap, pm_message_t mesg);
808c2ecf20Sopenharmony_ci#endif
818c2ecf20Sopenharmony_cistatic ssize_t ahci_activity_show(struct ata_device *dev, char *buf);
828c2ecf20Sopenharmony_cistatic ssize_t ahci_activity_store(struct ata_device *dev,
838c2ecf20Sopenharmony_ci				   enum sw_activity val);
848c2ecf20Sopenharmony_cistatic void ahci_init_sw_activity(struct ata_link *link);
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_cistatic ssize_t ahci_show_host_caps(struct device *dev,
878c2ecf20Sopenharmony_ci				   struct device_attribute *attr, char *buf);
888c2ecf20Sopenharmony_cistatic ssize_t ahci_show_host_cap2(struct device *dev,
898c2ecf20Sopenharmony_ci				   struct device_attribute *attr, char *buf);
908c2ecf20Sopenharmony_cistatic ssize_t ahci_show_host_version(struct device *dev,
918c2ecf20Sopenharmony_ci				      struct device_attribute *attr, char *buf);
928c2ecf20Sopenharmony_cistatic ssize_t ahci_show_port_cmd(struct device *dev,
938c2ecf20Sopenharmony_ci				  struct device_attribute *attr, char *buf);
948c2ecf20Sopenharmony_cistatic ssize_t ahci_read_em_buffer(struct device *dev,
958c2ecf20Sopenharmony_ci				   struct device_attribute *attr, char *buf);
968c2ecf20Sopenharmony_cistatic ssize_t ahci_store_em_buffer(struct device *dev,
978c2ecf20Sopenharmony_ci				    struct device_attribute *attr,
988c2ecf20Sopenharmony_ci				    const char *buf, size_t size);
998c2ecf20Sopenharmony_cistatic ssize_t ahci_show_em_supported(struct device *dev,
1008c2ecf20Sopenharmony_ci				      struct device_attribute *attr, char *buf);
1018c2ecf20Sopenharmony_cistatic irqreturn_t ahci_single_level_irq_intr(int irq, void *dev_instance);
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_cistatic DEVICE_ATTR(ahci_host_caps, S_IRUGO, ahci_show_host_caps, NULL);
1048c2ecf20Sopenharmony_cistatic DEVICE_ATTR(ahci_host_cap2, S_IRUGO, ahci_show_host_cap2, NULL);
1058c2ecf20Sopenharmony_cistatic DEVICE_ATTR(ahci_host_version, S_IRUGO, ahci_show_host_version, NULL);
1068c2ecf20Sopenharmony_cistatic DEVICE_ATTR(ahci_port_cmd, S_IRUGO, ahci_show_port_cmd, NULL);
1078c2ecf20Sopenharmony_cistatic DEVICE_ATTR(em_buffer, S_IWUSR | S_IRUGO,
1088c2ecf20Sopenharmony_ci		   ahci_read_em_buffer, ahci_store_em_buffer);
1098c2ecf20Sopenharmony_cistatic DEVICE_ATTR(em_message_supported, S_IRUGO, ahci_show_em_supported, NULL);
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_cistruct device_attribute *ahci_shost_attrs[] = {
1128c2ecf20Sopenharmony_ci	&dev_attr_link_power_management_policy,
1138c2ecf20Sopenharmony_ci	&dev_attr_em_message_type,
1148c2ecf20Sopenharmony_ci	&dev_attr_em_message,
1158c2ecf20Sopenharmony_ci	&dev_attr_ahci_host_caps,
1168c2ecf20Sopenharmony_ci	&dev_attr_ahci_host_cap2,
1178c2ecf20Sopenharmony_ci	&dev_attr_ahci_host_version,
1188c2ecf20Sopenharmony_ci	&dev_attr_ahci_port_cmd,
1198c2ecf20Sopenharmony_ci	&dev_attr_em_buffer,
1208c2ecf20Sopenharmony_ci	&dev_attr_em_message_supported,
1218c2ecf20Sopenharmony_ci	NULL
1228c2ecf20Sopenharmony_ci};
1238c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ahci_shost_attrs);
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_cistruct device_attribute *ahci_sdev_attrs[] = {
1268c2ecf20Sopenharmony_ci	&dev_attr_sw_activity,
1278c2ecf20Sopenharmony_ci	&dev_attr_unload_heads,
1288c2ecf20Sopenharmony_ci	&dev_attr_ncq_prio_enable,
1298c2ecf20Sopenharmony_ci	NULL
1308c2ecf20Sopenharmony_ci};
1318c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ahci_sdev_attrs);
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_cistruct ata_port_operations ahci_ops = {
1348c2ecf20Sopenharmony_ci	.inherits		= &sata_pmp_port_ops,
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	.qc_defer		= ahci_pmp_qc_defer,
1378c2ecf20Sopenharmony_ci	.qc_prep		= ahci_qc_prep,
1388c2ecf20Sopenharmony_ci	.qc_issue		= ahci_qc_issue,
1398c2ecf20Sopenharmony_ci	.qc_fill_rtf		= ahci_qc_fill_rtf,
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	.freeze			= ahci_freeze,
1428c2ecf20Sopenharmony_ci	.thaw			= ahci_thaw,
1438c2ecf20Sopenharmony_ci	.softreset		= ahci_softreset,
1448c2ecf20Sopenharmony_ci	.hardreset		= ahci_hardreset,
1458c2ecf20Sopenharmony_ci	.postreset		= ahci_postreset,
1468c2ecf20Sopenharmony_ci	.pmp_softreset		= ahci_softreset,
1478c2ecf20Sopenharmony_ci	.error_handler		= ahci_error_handler,
1488c2ecf20Sopenharmony_ci	.post_internal_cmd	= ahci_post_internal_cmd,
1498c2ecf20Sopenharmony_ci	.dev_config		= ahci_dev_config,
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	.scr_read		= ahci_scr_read,
1528c2ecf20Sopenharmony_ci	.scr_write		= ahci_scr_write,
1538c2ecf20Sopenharmony_ci	.pmp_attach		= ahci_pmp_attach,
1548c2ecf20Sopenharmony_ci	.pmp_detach		= ahci_pmp_detach,
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	.set_lpm		= ahci_set_lpm,
1578c2ecf20Sopenharmony_ci	.em_show		= ahci_led_show,
1588c2ecf20Sopenharmony_ci	.em_store		= ahci_led_store,
1598c2ecf20Sopenharmony_ci	.sw_activity_show	= ahci_activity_show,
1608c2ecf20Sopenharmony_ci	.sw_activity_store	= ahci_activity_store,
1618c2ecf20Sopenharmony_ci	.transmit_led_message	= ahci_transmit_led_message,
1628c2ecf20Sopenharmony_ci#ifdef CONFIG_PM
1638c2ecf20Sopenharmony_ci	.port_suspend		= ahci_port_suspend,
1648c2ecf20Sopenharmony_ci	.port_resume		= ahci_port_resume,
1658c2ecf20Sopenharmony_ci#endif
1668c2ecf20Sopenharmony_ci	.port_start		= ahci_port_start,
1678c2ecf20Sopenharmony_ci	.port_stop		= ahci_port_stop,
1688c2ecf20Sopenharmony_ci};
1698c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ahci_ops);
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_cistruct ata_port_operations ahci_pmp_retry_srst_ops = {
1728c2ecf20Sopenharmony_ci	.inherits		= &ahci_ops,
1738c2ecf20Sopenharmony_ci	.softreset		= ahci_pmp_retry_softreset,
1748c2ecf20Sopenharmony_ci};
1758c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ahci_pmp_retry_srst_ops);
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_cistatic bool ahci_em_messages __read_mostly = true;
1788c2ecf20Sopenharmony_cimodule_param(ahci_em_messages, bool, 0444);
1798c2ecf20Sopenharmony_ci/* add other LED protocol types when they become supported */
1808c2ecf20Sopenharmony_ciMODULE_PARM_DESC(ahci_em_messages,
1818c2ecf20Sopenharmony_ci	"AHCI Enclosure Management Message control (0 = off, 1 = on)");
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci/* device sleep idle timeout in ms */
1848c2ecf20Sopenharmony_cistatic int devslp_idle_timeout __read_mostly = 1000;
1858c2ecf20Sopenharmony_cimodule_param(devslp_idle_timeout, int, 0644);
1868c2ecf20Sopenharmony_ciMODULE_PARM_DESC(devslp_idle_timeout, "device sleep idle timeout");
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_cistatic void ahci_enable_ahci(void __iomem *mmio)
1898c2ecf20Sopenharmony_ci{
1908c2ecf20Sopenharmony_ci	int i;
1918c2ecf20Sopenharmony_ci	u32 tmp;
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	/* turn on AHCI_EN */
1948c2ecf20Sopenharmony_ci	tmp = readl(mmio + HOST_CTL);
1958c2ecf20Sopenharmony_ci	if (tmp & HOST_AHCI_EN)
1968c2ecf20Sopenharmony_ci		return;
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	/* Some controllers need AHCI_EN to be written multiple times.
1998c2ecf20Sopenharmony_ci	 * Try a few times before giving up.
2008c2ecf20Sopenharmony_ci	 */
2018c2ecf20Sopenharmony_ci	for (i = 0; i < 5; i++) {
2028c2ecf20Sopenharmony_ci		tmp |= HOST_AHCI_EN;
2038c2ecf20Sopenharmony_ci		writel(tmp, mmio + HOST_CTL);
2048c2ecf20Sopenharmony_ci		tmp = readl(mmio + HOST_CTL);	/* flush && sanity check */
2058c2ecf20Sopenharmony_ci		if (tmp & HOST_AHCI_EN)
2068c2ecf20Sopenharmony_ci			return;
2078c2ecf20Sopenharmony_ci		msleep(10);
2088c2ecf20Sopenharmony_ci	}
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	WARN_ON(1);
2118c2ecf20Sopenharmony_ci}
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci/**
2148c2ecf20Sopenharmony_ci *	ahci_rpm_get_port - Make sure the port is powered on
2158c2ecf20Sopenharmony_ci *	@ap: Port to power on
2168c2ecf20Sopenharmony_ci *
2178c2ecf20Sopenharmony_ci *	Whenever there is need to access the AHCI host registers outside of
2188c2ecf20Sopenharmony_ci *	normal execution paths, call this function to make sure the host is
2198c2ecf20Sopenharmony_ci *	actually powered on.
2208c2ecf20Sopenharmony_ci */
2218c2ecf20Sopenharmony_cistatic int ahci_rpm_get_port(struct ata_port *ap)
2228c2ecf20Sopenharmony_ci{
2238c2ecf20Sopenharmony_ci	return pm_runtime_get_sync(ap->dev);
2248c2ecf20Sopenharmony_ci}
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci/**
2278c2ecf20Sopenharmony_ci *	ahci_rpm_put_port - Undoes ahci_rpm_get_port()
2288c2ecf20Sopenharmony_ci *	@ap: Port to power down
2298c2ecf20Sopenharmony_ci *
2308c2ecf20Sopenharmony_ci *	Undoes ahci_rpm_get_port() and possibly powers down the AHCI host
2318c2ecf20Sopenharmony_ci *	if it has no more active users.
2328c2ecf20Sopenharmony_ci */
2338c2ecf20Sopenharmony_cistatic void ahci_rpm_put_port(struct ata_port *ap)
2348c2ecf20Sopenharmony_ci{
2358c2ecf20Sopenharmony_ci	pm_runtime_put(ap->dev);
2368c2ecf20Sopenharmony_ci}
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_cistatic ssize_t ahci_show_host_caps(struct device *dev,
2398c2ecf20Sopenharmony_ci				   struct device_attribute *attr, char *buf)
2408c2ecf20Sopenharmony_ci{
2418c2ecf20Sopenharmony_ci	struct Scsi_Host *shost = class_to_shost(dev);
2428c2ecf20Sopenharmony_ci	struct ata_port *ap = ata_shost_to_port(shost);
2438c2ecf20Sopenharmony_ci	struct ahci_host_priv *hpriv = ap->host->private_data;
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	return sprintf(buf, "%x\n", hpriv->cap);
2468c2ecf20Sopenharmony_ci}
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_cistatic ssize_t ahci_show_host_cap2(struct device *dev,
2498c2ecf20Sopenharmony_ci				   struct device_attribute *attr, char *buf)
2508c2ecf20Sopenharmony_ci{
2518c2ecf20Sopenharmony_ci	struct Scsi_Host *shost = class_to_shost(dev);
2528c2ecf20Sopenharmony_ci	struct ata_port *ap = ata_shost_to_port(shost);
2538c2ecf20Sopenharmony_ci	struct ahci_host_priv *hpriv = ap->host->private_data;
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	return sprintf(buf, "%x\n", hpriv->cap2);
2568c2ecf20Sopenharmony_ci}
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_cistatic ssize_t ahci_show_host_version(struct device *dev,
2598c2ecf20Sopenharmony_ci				   struct device_attribute *attr, char *buf)
2608c2ecf20Sopenharmony_ci{
2618c2ecf20Sopenharmony_ci	struct Scsi_Host *shost = class_to_shost(dev);
2628c2ecf20Sopenharmony_ci	struct ata_port *ap = ata_shost_to_port(shost);
2638c2ecf20Sopenharmony_ci	struct ahci_host_priv *hpriv = ap->host->private_data;
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	return sprintf(buf, "%x\n", hpriv->version);
2668c2ecf20Sopenharmony_ci}
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_cistatic ssize_t ahci_show_port_cmd(struct device *dev,
2698c2ecf20Sopenharmony_ci				  struct device_attribute *attr, char *buf)
2708c2ecf20Sopenharmony_ci{
2718c2ecf20Sopenharmony_ci	struct Scsi_Host *shost = class_to_shost(dev);
2728c2ecf20Sopenharmony_ci	struct ata_port *ap = ata_shost_to_port(shost);
2738c2ecf20Sopenharmony_ci	void __iomem *port_mmio = ahci_port_base(ap);
2748c2ecf20Sopenharmony_ci	ssize_t ret;
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	ahci_rpm_get_port(ap);
2778c2ecf20Sopenharmony_ci	ret = sprintf(buf, "%x\n", readl(port_mmio + PORT_CMD));
2788c2ecf20Sopenharmony_ci	ahci_rpm_put_port(ap);
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	return ret;
2818c2ecf20Sopenharmony_ci}
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_cistatic ssize_t ahci_read_em_buffer(struct device *dev,
2848c2ecf20Sopenharmony_ci				   struct device_attribute *attr, char *buf)
2858c2ecf20Sopenharmony_ci{
2868c2ecf20Sopenharmony_ci	struct Scsi_Host *shost = class_to_shost(dev);
2878c2ecf20Sopenharmony_ci	struct ata_port *ap = ata_shost_to_port(shost);
2888c2ecf20Sopenharmony_ci	struct ahci_host_priv *hpriv = ap->host->private_data;
2898c2ecf20Sopenharmony_ci	void __iomem *mmio = hpriv->mmio;
2908c2ecf20Sopenharmony_ci	void __iomem *em_mmio = mmio + hpriv->em_loc;
2918c2ecf20Sopenharmony_ci	u32 em_ctl, msg;
2928c2ecf20Sopenharmony_ci	unsigned long flags;
2938c2ecf20Sopenharmony_ci	size_t count;
2948c2ecf20Sopenharmony_ci	int i;
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	ahci_rpm_get_port(ap);
2978c2ecf20Sopenharmony_ci	spin_lock_irqsave(ap->lock, flags);
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	em_ctl = readl(mmio + HOST_EM_CTL);
3008c2ecf20Sopenharmony_ci	if (!(ap->flags & ATA_FLAG_EM) || em_ctl & EM_CTL_XMT ||
3018c2ecf20Sopenharmony_ci	    !(hpriv->em_msg_type & EM_MSG_TYPE_SGPIO)) {
3028c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(ap->lock, flags);
3038c2ecf20Sopenharmony_ci		ahci_rpm_put_port(ap);
3048c2ecf20Sopenharmony_ci		return -EINVAL;
3058c2ecf20Sopenharmony_ci	}
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	if (!(em_ctl & EM_CTL_MR)) {
3088c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(ap->lock, flags);
3098c2ecf20Sopenharmony_ci		ahci_rpm_put_port(ap);
3108c2ecf20Sopenharmony_ci		return -EAGAIN;
3118c2ecf20Sopenharmony_ci	}
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci	if (!(em_ctl & EM_CTL_SMB))
3148c2ecf20Sopenharmony_ci		em_mmio += hpriv->em_buf_sz;
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	count = hpriv->em_buf_sz;
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	/* the count should not be larger than PAGE_SIZE */
3198c2ecf20Sopenharmony_ci	if (count > PAGE_SIZE) {
3208c2ecf20Sopenharmony_ci		if (printk_ratelimit())
3218c2ecf20Sopenharmony_ci			ata_port_warn(ap,
3228c2ecf20Sopenharmony_ci				      "EM read buffer size too large: "
3238c2ecf20Sopenharmony_ci				      "buffer size %u, page size %lu\n",
3248c2ecf20Sopenharmony_ci				      hpriv->em_buf_sz, PAGE_SIZE);
3258c2ecf20Sopenharmony_ci		count = PAGE_SIZE;
3268c2ecf20Sopenharmony_ci	}
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci	for (i = 0; i < count; i += 4) {
3298c2ecf20Sopenharmony_ci		msg = readl(em_mmio + i);
3308c2ecf20Sopenharmony_ci		buf[i] = msg & 0xff;
3318c2ecf20Sopenharmony_ci		buf[i + 1] = (msg >> 8) & 0xff;
3328c2ecf20Sopenharmony_ci		buf[i + 2] = (msg >> 16) & 0xff;
3338c2ecf20Sopenharmony_ci		buf[i + 3] = (msg >> 24) & 0xff;
3348c2ecf20Sopenharmony_ci	}
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(ap->lock, flags);
3378c2ecf20Sopenharmony_ci	ahci_rpm_put_port(ap);
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	return i;
3408c2ecf20Sopenharmony_ci}
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_cistatic ssize_t ahci_store_em_buffer(struct device *dev,
3438c2ecf20Sopenharmony_ci				    struct device_attribute *attr,
3448c2ecf20Sopenharmony_ci				    const char *buf, size_t size)
3458c2ecf20Sopenharmony_ci{
3468c2ecf20Sopenharmony_ci	struct Scsi_Host *shost = class_to_shost(dev);
3478c2ecf20Sopenharmony_ci	struct ata_port *ap = ata_shost_to_port(shost);
3488c2ecf20Sopenharmony_ci	struct ahci_host_priv *hpriv = ap->host->private_data;
3498c2ecf20Sopenharmony_ci	void __iomem *mmio = hpriv->mmio;
3508c2ecf20Sopenharmony_ci	void __iomem *em_mmio = mmio + hpriv->em_loc;
3518c2ecf20Sopenharmony_ci	const unsigned char *msg_buf = buf;
3528c2ecf20Sopenharmony_ci	u32 em_ctl, msg;
3538c2ecf20Sopenharmony_ci	unsigned long flags;
3548c2ecf20Sopenharmony_ci	int i;
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	/* check size validity */
3578c2ecf20Sopenharmony_ci	if (!(ap->flags & ATA_FLAG_EM) ||
3588c2ecf20Sopenharmony_ci	    !(hpriv->em_msg_type & EM_MSG_TYPE_SGPIO) ||
3598c2ecf20Sopenharmony_ci	    size % 4 || size > hpriv->em_buf_sz)
3608c2ecf20Sopenharmony_ci		return -EINVAL;
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	ahci_rpm_get_port(ap);
3638c2ecf20Sopenharmony_ci	spin_lock_irqsave(ap->lock, flags);
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	em_ctl = readl(mmio + HOST_EM_CTL);
3668c2ecf20Sopenharmony_ci	if (em_ctl & EM_CTL_TM) {
3678c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(ap->lock, flags);
3688c2ecf20Sopenharmony_ci		ahci_rpm_put_port(ap);
3698c2ecf20Sopenharmony_ci		return -EBUSY;
3708c2ecf20Sopenharmony_ci	}
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	for (i = 0; i < size; i += 4) {
3738c2ecf20Sopenharmony_ci		msg = msg_buf[i] | msg_buf[i + 1] << 8 |
3748c2ecf20Sopenharmony_ci		      msg_buf[i + 2] << 16 | msg_buf[i + 3] << 24;
3758c2ecf20Sopenharmony_ci		writel(msg, em_mmio + i);
3768c2ecf20Sopenharmony_ci	}
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	writel(em_ctl | EM_CTL_TM, mmio + HOST_EM_CTL);
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(ap->lock, flags);
3818c2ecf20Sopenharmony_ci	ahci_rpm_put_port(ap);
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	return size;
3848c2ecf20Sopenharmony_ci}
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_cistatic ssize_t ahci_show_em_supported(struct device *dev,
3878c2ecf20Sopenharmony_ci				      struct device_attribute *attr, char *buf)
3888c2ecf20Sopenharmony_ci{
3898c2ecf20Sopenharmony_ci	struct Scsi_Host *shost = class_to_shost(dev);
3908c2ecf20Sopenharmony_ci	struct ata_port *ap = ata_shost_to_port(shost);
3918c2ecf20Sopenharmony_ci	struct ahci_host_priv *hpriv = ap->host->private_data;
3928c2ecf20Sopenharmony_ci	void __iomem *mmio = hpriv->mmio;
3938c2ecf20Sopenharmony_ci	u32 em_ctl;
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	ahci_rpm_get_port(ap);
3968c2ecf20Sopenharmony_ci	em_ctl = readl(mmio + HOST_EM_CTL);
3978c2ecf20Sopenharmony_ci	ahci_rpm_put_port(ap);
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci	return sprintf(buf, "%s%s%s%s\n",
4008c2ecf20Sopenharmony_ci		       em_ctl & EM_CTL_LED ? "led " : "",
4018c2ecf20Sopenharmony_ci		       em_ctl & EM_CTL_SAFTE ? "saf-te " : "",
4028c2ecf20Sopenharmony_ci		       em_ctl & EM_CTL_SES ? "ses-2 " : "",
4038c2ecf20Sopenharmony_ci		       em_ctl & EM_CTL_SGPIO ? "sgpio " : "");
4048c2ecf20Sopenharmony_ci}
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci/**
4078c2ecf20Sopenharmony_ci *	ahci_save_initial_config - Save and fixup initial config values
4088c2ecf20Sopenharmony_ci *	@dev: target AHCI device
4098c2ecf20Sopenharmony_ci *	@hpriv: host private area to store config values
4108c2ecf20Sopenharmony_ci *
4118c2ecf20Sopenharmony_ci *	Some registers containing configuration info might be setup by
4128c2ecf20Sopenharmony_ci *	BIOS and might be cleared on reset.  This function saves the
4138c2ecf20Sopenharmony_ci *	initial values of those registers into @hpriv such that they
4148c2ecf20Sopenharmony_ci *	can be restored after controller reset.
4158c2ecf20Sopenharmony_ci *
4168c2ecf20Sopenharmony_ci *	If inconsistent, config values are fixed up by this function.
4178c2ecf20Sopenharmony_ci *
4188c2ecf20Sopenharmony_ci *	If it is not set already this function sets hpriv->start_engine to
4198c2ecf20Sopenharmony_ci *	ahci_start_engine.
4208c2ecf20Sopenharmony_ci *
4218c2ecf20Sopenharmony_ci *	LOCKING:
4228c2ecf20Sopenharmony_ci *	None.
4238c2ecf20Sopenharmony_ci */
4248c2ecf20Sopenharmony_civoid ahci_save_initial_config(struct device *dev, struct ahci_host_priv *hpriv)
4258c2ecf20Sopenharmony_ci{
4268c2ecf20Sopenharmony_ci	void __iomem *mmio = hpriv->mmio;
4278c2ecf20Sopenharmony_ci	u32 cap, cap2, vers, port_map;
4288c2ecf20Sopenharmony_ci	int i;
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci	/* make sure AHCI mode is enabled before accessing CAP */
4318c2ecf20Sopenharmony_ci	ahci_enable_ahci(mmio);
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci	/* Values prefixed with saved_ are written back to host after
4348c2ecf20Sopenharmony_ci	 * reset.  Values without are used for driver operation.
4358c2ecf20Sopenharmony_ci	 */
4368c2ecf20Sopenharmony_ci	hpriv->saved_cap = cap = readl(mmio + HOST_CAP);
4378c2ecf20Sopenharmony_ci	hpriv->saved_port_map = port_map = readl(mmio + HOST_PORTS_IMPL);
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci	/* CAP2 register is only defined for AHCI 1.2 and later */
4408c2ecf20Sopenharmony_ci	vers = readl(mmio + HOST_VERSION);
4418c2ecf20Sopenharmony_ci	if ((vers >> 16) > 1 ||
4428c2ecf20Sopenharmony_ci	   ((vers >> 16) == 1 && (vers & 0xFFFF) >= 0x200))
4438c2ecf20Sopenharmony_ci		hpriv->saved_cap2 = cap2 = readl(mmio + HOST_CAP2);
4448c2ecf20Sopenharmony_ci	else
4458c2ecf20Sopenharmony_ci		hpriv->saved_cap2 = cap2 = 0;
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ci	/* some chips have errata preventing 64bit use */
4488c2ecf20Sopenharmony_ci	if ((cap & HOST_CAP_64) && (hpriv->flags & AHCI_HFLAG_32BIT_ONLY)) {
4498c2ecf20Sopenharmony_ci		dev_info(dev, "controller can't do 64bit DMA, forcing 32bit\n");
4508c2ecf20Sopenharmony_ci		cap &= ~HOST_CAP_64;
4518c2ecf20Sopenharmony_ci	}
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	if ((cap & HOST_CAP_NCQ) && (hpriv->flags & AHCI_HFLAG_NO_NCQ)) {
4548c2ecf20Sopenharmony_ci		dev_info(dev, "controller can't do NCQ, turning off CAP_NCQ\n");
4558c2ecf20Sopenharmony_ci		cap &= ~HOST_CAP_NCQ;
4568c2ecf20Sopenharmony_ci	}
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci	if (!(cap & HOST_CAP_NCQ) && (hpriv->flags & AHCI_HFLAG_YES_NCQ)) {
4598c2ecf20Sopenharmony_ci		dev_info(dev, "controller can do NCQ, turning on CAP_NCQ\n");
4608c2ecf20Sopenharmony_ci		cap |= HOST_CAP_NCQ;
4618c2ecf20Sopenharmony_ci	}
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci	if ((cap & HOST_CAP_PMP) && (hpriv->flags & AHCI_HFLAG_NO_PMP)) {
4648c2ecf20Sopenharmony_ci		dev_info(dev, "controller can't do PMP, turning off CAP_PMP\n");
4658c2ecf20Sopenharmony_ci		cap &= ~HOST_CAP_PMP;
4668c2ecf20Sopenharmony_ci	}
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci	if ((cap & HOST_CAP_SNTF) && (hpriv->flags & AHCI_HFLAG_NO_SNTF)) {
4698c2ecf20Sopenharmony_ci		dev_info(dev,
4708c2ecf20Sopenharmony_ci			 "controller can't do SNTF, turning off CAP_SNTF\n");
4718c2ecf20Sopenharmony_ci		cap &= ~HOST_CAP_SNTF;
4728c2ecf20Sopenharmony_ci	}
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci	if ((cap2 & HOST_CAP2_SDS) && (hpriv->flags & AHCI_HFLAG_NO_DEVSLP)) {
4758c2ecf20Sopenharmony_ci		dev_info(dev,
4768c2ecf20Sopenharmony_ci			 "controller can't do DEVSLP, turning off\n");
4778c2ecf20Sopenharmony_ci		cap2 &= ~HOST_CAP2_SDS;
4788c2ecf20Sopenharmony_ci		cap2 &= ~HOST_CAP2_SADM;
4798c2ecf20Sopenharmony_ci	}
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ci	if (!(cap & HOST_CAP_FBS) && (hpriv->flags & AHCI_HFLAG_YES_FBS)) {
4828c2ecf20Sopenharmony_ci		dev_info(dev, "controller can do FBS, turning on CAP_FBS\n");
4838c2ecf20Sopenharmony_ci		cap |= HOST_CAP_FBS;
4848c2ecf20Sopenharmony_ci	}
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci	if ((cap & HOST_CAP_FBS) && (hpriv->flags & AHCI_HFLAG_NO_FBS)) {
4878c2ecf20Sopenharmony_ci		dev_info(dev, "controller can't do FBS, turning off CAP_FBS\n");
4888c2ecf20Sopenharmony_ci		cap &= ~HOST_CAP_FBS;
4898c2ecf20Sopenharmony_ci	}
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci	if (!(cap & HOST_CAP_ALPM) && (hpriv->flags & AHCI_HFLAG_YES_ALPM)) {
4928c2ecf20Sopenharmony_ci		dev_info(dev, "controller can do ALPM, turning on CAP_ALPM\n");
4938c2ecf20Sopenharmony_ci		cap |= HOST_CAP_ALPM;
4948c2ecf20Sopenharmony_ci	}
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci	if ((cap & HOST_CAP_SXS) && (hpriv->flags & AHCI_HFLAG_NO_SXS)) {
4978c2ecf20Sopenharmony_ci		dev_info(dev, "controller does not support SXS, disabling CAP_SXS\n");
4988c2ecf20Sopenharmony_ci		cap &= ~HOST_CAP_SXS;
4998c2ecf20Sopenharmony_ci	}
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_ci	if (hpriv->force_port_map && port_map != hpriv->force_port_map) {
5028c2ecf20Sopenharmony_ci		dev_info(dev, "forcing port_map 0x%x -> 0x%x\n",
5038c2ecf20Sopenharmony_ci			 port_map, hpriv->force_port_map);
5048c2ecf20Sopenharmony_ci		port_map = hpriv->force_port_map;
5058c2ecf20Sopenharmony_ci		hpriv->saved_port_map = port_map;
5068c2ecf20Sopenharmony_ci	}
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ci	if (hpriv->mask_port_map) {
5098c2ecf20Sopenharmony_ci		dev_warn(dev, "masking port_map 0x%x -> 0x%x\n",
5108c2ecf20Sopenharmony_ci			port_map,
5118c2ecf20Sopenharmony_ci			port_map & hpriv->mask_port_map);
5128c2ecf20Sopenharmony_ci		port_map &= hpriv->mask_port_map;
5138c2ecf20Sopenharmony_ci	}
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_ci	/* cross check port_map and cap.n_ports */
5168c2ecf20Sopenharmony_ci	if (port_map) {
5178c2ecf20Sopenharmony_ci		int map_ports = 0;
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_ci		for (i = 0; i < AHCI_MAX_PORTS; i++)
5208c2ecf20Sopenharmony_ci			if (port_map & (1 << i))
5218c2ecf20Sopenharmony_ci				map_ports++;
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_ci		/* If PI has more ports than n_ports, whine, clear
5248c2ecf20Sopenharmony_ci		 * port_map and let it be generated from n_ports.
5258c2ecf20Sopenharmony_ci		 */
5268c2ecf20Sopenharmony_ci		if (map_ports > ahci_nr_ports(cap)) {
5278c2ecf20Sopenharmony_ci			dev_warn(dev,
5288c2ecf20Sopenharmony_ci				 "implemented port map (0x%x) contains more ports than nr_ports (%u), using nr_ports\n",
5298c2ecf20Sopenharmony_ci				 port_map, ahci_nr_ports(cap));
5308c2ecf20Sopenharmony_ci			port_map = 0;
5318c2ecf20Sopenharmony_ci		}
5328c2ecf20Sopenharmony_ci	}
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_ci	/* fabricate port_map from cap.nr_ports for < AHCI 1.3 */
5358c2ecf20Sopenharmony_ci	if (!port_map && vers < 0x10300) {
5368c2ecf20Sopenharmony_ci		port_map = (1 << ahci_nr_ports(cap)) - 1;
5378c2ecf20Sopenharmony_ci		dev_warn(dev, "forcing PORTS_IMPL to 0x%x\n", port_map);
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ci		/* write the fixed up value to the PI register */
5408c2ecf20Sopenharmony_ci		hpriv->saved_port_map = port_map;
5418c2ecf20Sopenharmony_ci	}
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_ci	/* record values to use during operation */
5448c2ecf20Sopenharmony_ci	hpriv->cap = cap;
5458c2ecf20Sopenharmony_ci	hpriv->cap2 = cap2;
5468c2ecf20Sopenharmony_ci	hpriv->version = readl(mmio + HOST_VERSION);
5478c2ecf20Sopenharmony_ci	hpriv->port_map = port_map;
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_ci	if (!hpriv->start_engine)
5508c2ecf20Sopenharmony_ci		hpriv->start_engine = ahci_start_engine;
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_ci	if (!hpriv->stop_engine)
5538c2ecf20Sopenharmony_ci		hpriv->stop_engine = ahci_stop_engine;
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_ci	if (!hpriv->irq_handler)
5568c2ecf20Sopenharmony_ci		hpriv->irq_handler = ahci_single_level_irq_intr;
5578c2ecf20Sopenharmony_ci}
5588c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ahci_save_initial_config);
5598c2ecf20Sopenharmony_ci
5608c2ecf20Sopenharmony_ci/**
5618c2ecf20Sopenharmony_ci *	ahci_restore_initial_config - Restore initial config
5628c2ecf20Sopenharmony_ci *	@host: target ATA host
5638c2ecf20Sopenharmony_ci *
5648c2ecf20Sopenharmony_ci *	Restore initial config stored by ahci_save_initial_config().
5658c2ecf20Sopenharmony_ci *
5668c2ecf20Sopenharmony_ci *	LOCKING:
5678c2ecf20Sopenharmony_ci *	None.
5688c2ecf20Sopenharmony_ci */
5698c2ecf20Sopenharmony_cistatic void ahci_restore_initial_config(struct ata_host *host)
5708c2ecf20Sopenharmony_ci{
5718c2ecf20Sopenharmony_ci	struct ahci_host_priv *hpriv = host->private_data;
5728c2ecf20Sopenharmony_ci	void __iomem *mmio = hpriv->mmio;
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_ci	writel(hpriv->saved_cap, mmio + HOST_CAP);
5758c2ecf20Sopenharmony_ci	if (hpriv->saved_cap2)
5768c2ecf20Sopenharmony_ci		writel(hpriv->saved_cap2, mmio + HOST_CAP2);
5778c2ecf20Sopenharmony_ci	writel(hpriv->saved_port_map, mmio + HOST_PORTS_IMPL);
5788c2ecf20Sopenharmony_ci	(void) readl(mmio + HOST_PORTS_IMPL);	/* flush */
5798c2ecf20Sopenharmony_ci}
5808c2ecf20Sopenharmony_ci
5818c2ecf20Sopenharmony_cistatic unsigned ahci_scr_offset(struct ata_port *ap, unsigned int sc_reg)
5828c2ecf20Sopenharmony_ci{
5838c2ecf20Sopenharmony_ci	static const int offset[] = {
5848c2ecf20Sopenharmony_ci		[SCR_STATUS]		= PORT_SCR_STAT,
5858c2ecf20Sopenharmony_ci		[SCR_CONTROL]		= PORT_SCR_CTL,
5868c2ecf20Sopenharmony_ci		[SCR_ERROR]		= PORT_SCR_ERR,
5878c2ecf20Sopenharmony_ci		[SCR_ACTIVE]		= PORT_SCR_ACT,
5888c2ecf20Sopenharmony_ci		[SCR_NOTIFICATION]	= PORT_SCR_NTF,
5898c2ecf20Sopenharmony_ci	};
5908c2ecf20Sopenharmony_ci	struct ahci_host_priv *hpriv = ap->host->private_data;
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_ci	if (sc_reg < ARRAY_SIZE(offset) &&
5938c2ecf20Sopenharmony_ci	    (sc_reg != SCR_NOTIFICATION || (hpriv->cap & HOST_CAP_SNTF)))
5948c2ecf20Sopenharmony_ci		return offset[sc_reg];
5958c2ecf20Sopenharmony_ci	return 0;
5968c2ecf20Sopenharmony_ci}
5978c2ecf20Sopenharmony_ci
5988c2ecf20Sopenharmony_cistatic int ahci_scr_read(struct ata_link *link, unsigned int sc_reg, u32 *val)
5998c2ecf20Sopenharmony_ci{
6008c2ecf20Sopenharmony_ci	void __iomem *port_mmio = ahci_port_base(link->ap);
6018c2ecf20Sopenharmony_ci	int offset = ahci_scr_offset(link->ap, sc_reg);
6028c2ecf20Sopenharmony_ci
6038c2ecf20Sopenharmony_ci	if (offset) {
6048c2ecf20Sopenharmony_ci		*val = readl(port_mmio + offset);
6058c2ecf20Sopenharmony_ci		return 0;
6068c2ecf20Sopenharmony_ci	}
6078c2ecf20Sopenharmony_ci	return -EINVAL;
6088c2ecf20Sopenharmony_ci}
6098c2ecf20Sopenharmony_ci
6108c2ecf20Sopenharmony_cistatic int ahci_scr_write(struct ata_link *link, unsigned int sc_reg, u32 val)
6118c2ecf20Sopenharmony_ci{
6128c2ecf20Sopenharmony_ci	void __iomem *port_mmio = ahci_port_base(link->ap);
6138c2ecf20Sopenharmony_ci	int offset = ahci_scr_offset(link->ap, sc_reg);
6148c2ecf20Sopenharmony_ci
6158c2ecf20Sopenharmony_ci	if (offset) {
6168c2ecf20Sopenharmony_ci		writel(val, port_mmio + offset);
6178c2ecf20Sopenharmony_ci		return 0;
6188c2ecf20Sopenharmony_ci	}
6198c2ecf20Sopenharmony_ci	return -EINVAL;
6208c2ecf20Sopenharmony_ci}
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_civoid ahci_start_engine(struct ata_port *ap)
6238c2ecf20Sopenharmony_ci{
6248c2ecf20Sopenharmony_ci	void __iomem *port_mmio = ahci_port_base(ap);
6258c2ecf20Sopenharmony_ci	u32 tmp;
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_ci	/* start DMA */
6288c2ecf20Sopenharmony_ci	tmp = readl(port_mmio + PORT_CMD);
6298c2ecf20Sopenharmony_ci	tmp |= PORT_CMD_START;
6308c2ecf20Sopenharmony_ci	writel(tmp, port_mmio + PORT_CMD);
6318c2ecf20Sopenharmony_ci	readl(port_mmio + PORT_CMD); /* flush */
6328c2ecf20Sopenharmony_ci}
6338c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ahci_start_engine);
6348c2ecf20Sopenharmony_ci
6358c2ecf20Sopenharmony_ciint ahci_stop_engine(struct ata_port *ap)
6368c2ecf20Sopenharmony_ci{
6378c2ecf20Sopenharmony_ci	void __iomem *port_mmio = ahci_port_base(ap);
6388c2ecf20Sopenharmony_ci	struct ahci_host_priv *hpriv = ap->host->private_data;
6398c2ecf20Sopenharmony_ci	u32 tmp;
6408c2ecf20Sopenharmony_ci
6418c2ecf20Sopenharmony_ci	/*
6428c2ecf20Sopenharmony_ci	 * On some controllers, stopping a port's DMA engine while the port
6438c2ecf20Sopenharmony_ci	 * is in ALPM state (partial or slumber) results in failures on
6448c2ecf20Sopenharmony_ci	 * subsequent DMA engine starts.  For those controllers, put the
6458c2ecf20Sopenharmony_ci	 * port back in active state before stopping its DMA engine.
6468c2ecf20Sopenharmony_ci	 */
6478c2ecf20Sopenharmony_ci	if ((hpriv->flags & AHCI_HFLAG_WAKE_BEFORE_STOP) &&
6488c2ecf20Sopenharmony_ci	    (ap->link.lpm_policy > ATA_LPM_MAX_POWER) &&
6498c2ecf20Sopenharmony_ci	    ahci_set_lpm(&ap->link, ATA_LPM_MAX_POWER, ATA_LPM_WAKE_ONLY)) {
6508c2ecf20Sopenharmony_ci		dev_err(ap->host->dev, "Failed to wake up port before engine stop\n");
6518c2ecf20Sopenharmony_ci		return -EIO;
6528c2ecf20Sopenharmony_ci	}
6538c2ecf20Sopenharmony_ci
6548c2ecf20Sopenharmony_ci	tmp = readl(port_mmio + PORT_CMD);
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_ci	/* check if the HBA is idle */
6578c2ecf20Sopenharmony_ci	if ((tmp & (PORT_CMD_START | PORT_CMD_LIST_ON)) == 0)
6588c2ecf20Sopenharmony_ci		return 0;
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_ci	/*
6618c2ecf20Sopenharmony_ci	 * Don't try to issue commands but return with ENODEV if the
6628c2ecf20Sopenharmony_ci	 * AHCI controller not available anymore (e.g. due to PCIe hot
6638c2ecf20Sopenharmony_ci	 * unplugging). Otherwise a 500ms delay for each port is added.
6648c2ecf20Sopenharmony_ci	 */
6658c2ecf20Sopenharmony_ci	if (tmp == 0xffffffff) {
6668c2ecf20Sopenharmony_ci		dev_err(ap->host->dev, "AHCI controller unavailable!\n");
6678c2ecf20Sopenharmony_ci		return -ENODEV;
6688c2ecf20Sopenharmony_ci	}
6698c2ecf20Sopenharmony_ci
6708c2ecf20Sopenharmony_ci	/* setting HBA to idle */
6718c2ecf20Sopenharmony_ci	tmp &= ~PORT_CMD_START;
6728c2ecf20Sopenharmony_ci	writel(tmp, port_mmio + PORT_CMD);
6738c2ecf20Sopenharmony_ci
6748c2ecf20Sopenharmony_ci	/* wait for engine to stop. This could be as long as 500 msec */
6758c2ecf20Sopenharmony_ci	tmp = ata_wait_register(ap, port_mmio + PORT_CMD,
6768c2ecf20Sopenharmony_ci				PORT_CMD_LIST_ON, PORT_CMD_LIST_ON, 1, 500);
6778c2ecf20Sopenharmony_ci	if (tmp & PORT_CMD_LIST_ON)
6788c2ecf20Sopenharmony_ci		return -EIO;
6798c2ecf20Sopenharmony_ci
6808c2ecf20Sopenharmony_ci	return 0;
6818c2ecf20Sopenharmony_ci}
6828c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ahci_stop_engine);
6838c2ecf20Sopenharmony_ci
6848c2ecf20Sopenharmony_civoid ahci_start_fis_rx(struct ata_port *ap)
6858c2ecf20Sopenharmony_ci{
6868c2ecf20Sopenharmony_ci	void __iomem *port_mmio = ahci_port_base(ap);
6878c2ecf20Sopenharmony_ci	struct ahci_host_priv *hpriv = ap->host->private_data;
6888c2ecf20Sopenharmony_ci	struct ahci_port_priv *pp = ap->private_data;
6898c2ecf20Sopenharmony_ci	u32 tmp;
6908c2ecf20Sopenharmony_ci
6918c2ecf20Sopenharmony_ci	/* set FIS registers */
6928c2ecf20Sopenharmony_ci	if (hpriv->cap & HOST_CAP_64)
6938c2ecf20Sopenharmony_ci		writel((pp->cmd_slot_dma >> 16) >> 16,
6948c2ecf20Sopenharmony_ci		       port_mmio + PORT_LST_ADDR_HI);
6958c2ecf20Sopenharmony_ci	writel(pp->cmd_slot_dma & 0xffffffff, port_mmio + PORT_LST_ADDR);
6968c2ecf20Sopenharmony_ci
6978c2ecf20Sopenharmony_ci	if (hpriv->cap & HOST_CAP_64)
6988c2ecf20Sopenharmony_ci		writel((pp->rx_fis_dma >> 16) >> 16,
6998c2ecf20Sopenharmony_ci		       port_mmio + PORT_FIS_ADDR_HI);
7008c2ecf20Sopenharmony_ci	writel(pp->rx_fis_dma & 0xffffffff, port_mmio + PORT_FIS_ADDR);
7018c2ecf20Sopenharmony_ci
7028c2ecf20Sopenharmony_ci	/* enable FIS reception */
7038c2ecf20Sopenharmony_ci	tmp = readl(port_mmio + PORT_CMD);
7048c2ecf20Sopenharmony_ci	tmp |= PORT_CMD_FIS_RX;
7058c2ecf20Sopenharmony_ci	writel(tmp, port_mmio + PORT_CMD);
7068c2ecf20Sopenharmony_ci
7078c2ecf20Sopenharmony_ci	/* flush */
7088c2ecf20Sopenharmony_ci	readl(port_mmio + PORT_CMD);
7098c2ecf20Sopenharmony_ci}
7108c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ahci_start_fis_rx);
7118c2ecf20Sopenharmony_ci
7128c2ecf20Sopenharmony_cistatic int ahci_stop_fis_rx(struct ata_port *ap)
7138c2ecf20Sopenharmony_ci{
7148c2ecf20Sopenharmony_ci	void __iomem *port_mmio = ahci_port_base(ap);
7158c2ecf20Sopenharmony_ci	u32 tmp;
7168c2ecf20Sopenharmony_ci
7178c2ecf20Sopenharmony_ci	/* disable FIS reception */
7188c2ecf20Sopenharmony_ci	tmp = readl(port_mmio + PORT_CMD);
7198c2ecf20Sopenharmony_ci	tmp &= ~PORT_CMD_FIS_RX;
7208c2ecf20Sopenharmony_ci	writel(tmp, port_mmio + PORT_CMD);
7218c2ecf20Sopenharmony_ci
7228c2ecf20Sopenharmony_ci	/* wait for completion, spec says 500ms, give it 1000 */
7238c2ecf20Sopenharmony_ci	tmp = ata_wait_register(ap, port_mmio + PORT_CMD, PORT_CMD_FIS_ON,
7248c2ecf20Sopenharmony_ci				PORT_CMD_FIS_ON, 10, 1000);
7258c2ecf20Sopenharmony_ci	if (tmp & PORT_CMD_FIS_ON)
7268c2ecf20Sopenharmony_ci		return -EBUSY;
7278c2ecf20Sopenharmony_ci
7288c2ecf20Sopenharmony_ci	return 0;
7298c2ecf20Sopenharmony_ci}
7308c2ecf20Sopenharmony_ci
7318c2ecf20Sopenharmony_cistatic void ahci_power_up(struct ata_port *ap)
7328c2ecf20Sopenharmony_ci{
7338c2ecf20Sopenharmony_ci	struct ahci_host_priv *hpriv = ap->host->private_data;
7348c2ecf20Sopenharmony_ci	void __iomem *port_mmio = ahci_port_base(ap);
7358c2ecf20Sopenharmony_ci	u32 cmd;
7368c2ecf20Sopenharmony_ci
7378c2ecf20Sopenharmony_ci	cmd = readl(port_mmio + PORT_CMD) & ~PORT_CMD_ICC_MASK;
7388c2ecf20Sopenharmony_ci
7398c2ecf20Sopenharmony_ci	/* spin up device */
7408c2ecf20Sopenharmony_ci	if (hpriv->cap & HOST_CAP_SSS) {
7418c2ecf20Sopenharmony_ci		cmd |= PORT_CMD_SPIN_UP;
7428c2ecf20Sopenharmony_ci		writel(cmd, port_mmio + PORT_CMD);
7438c2ecf20Sopenharmony_ci	}
7448c2ecf20Sopenharmony_ci
7458c2ecf20Sopenharmony_ci	/* wake up link */
7468c2ecf20Sopenharmony_ci	writel(cmd | PORT_CMD_ICC_ACTIVE, port_mmio + PORT_CMD);
7478c2ecf20Sopenharmony_ci}
7488c2ecf20Sopenharmony_ci
7498c2ecf20Sopenharmony_cistatic int ahci_set_lpm(struct ata_link *link, enum ata_lpm_policy policy,
7508c2ecf20Sopenharmony_ci			unsigned int hints)
7518c2ecf20Sopenharmony_ci{
7528c2ecf20Sopenharmony_ci	struct ata_port *ap = link->ap;
7538c2ecf20Sopenharmony_ci	struct ahci_host_priv *hpriv = ap->host->private_data;
7548c2ecf20Sopenharmony_ci	struct ahci_port_priv *pp = ap->private_data;
7558c2ecf20Sopenharmony_ci	void __iomem *port_mmio = ahci_port_base(ap);
7568c2ecf20Sopenharmony_ci
7578c2ecf20Sopenharmony_ci	if (policy != ATA_LPM_MAX_POWER) {
7588c2ecf20Sopenharmony_ci		/* wakeup flag only applies to the max power policy */
7598c2ecf20Sopenharmony_ci		hints &= ~ATA_LPM_WAKE_ONLY;
7608c2ecf20Sopenharmony_ci
7618c2ecf20Sopenharmony_ci		/*
7628c2ecf20Sopenharmony_ci		 * Disable interrupts on Phy Ready. This keeps us from
7638c2ecf20Sopenharmony_ci		 * getting woken up due to spurious phy ready
7648c2ecf20Sopenharmony_ci		 * interrupts.
7658c2ecf20Sopenharmony_ci		 */
7668c2ecf20Sopenharmony_ci		pp->intr_mask &= ~PORT_IRQ_PHYRDY;
7678c2ecf20Sopenharmony_ci		writel(pp->intr_mask, port_mmio + PORT_IRQ_MASK);
7688c2ecf20Sopenharmony_ci
7698c2ecf20Sopenharmony_ci		sata_link_scr_lpm(link, policy, false);
7708c2ecf20Sopenharmony_ci	}
7718c2ecf20Sopenharmony_ci
7728c2ecf20Sopenharmony_ci	if (hpriv->cap & HOST_CAP_ALPM) {
7738c2ecf20Sopenharmony_ci		u32 cmd = readl(port_mmio + PORT_CMD);
7748c2ecf20Sopenharmony_ci
7758c2ecf20Sopenharmony_ci		if (policy == ATA_LPM_MAX_POWER || !(hints & ATA_LPM_HIPM)) {
7768c2ecf20Sopenharmony_ci			if (!(hints & ATA_LPM_WAKE_ONLY))
7778c2ecf20Sopenharmony_ci				cmd &= ~(PORT_CMD_ASP | PORT_CMD_ALPE);
7788c2ecf20Sopenharmony_ci			cmd |= PORT_CMD_ICC_ACTIVE;
7798c2ecf20Sopenharmony_ci
7808c2ecf20Sopenharmony_ci			writel(cmd, port_mmio + PORT_CMD);
7818c2ecf20Sopenharmony_ci			readl(port_mmio + PORT_CMD);
7828c2ecf20Sopenharmony_ci
7838c2ecf20Sopenharmony_ci			/* wait 10ms to be sure we've come out of LPM state */
7848c2ecf20Sopenharmony_ci			ata_msleep(ap, 10);
7858c2ecf20Sopenharmony_ci
7868c2ecf20Sopenharmony_ci			if (hints & ATA_LPM_WAKE_ONLY)
7878c2ecf20Sopenharmony_ci				return 0;
7888c2ecf20Sopenharmony_ci		} else {
7898c2ecf20Sopenharmony_ci			cmd |= PORT_CMD_ALPE;
7908c2ecf20Sopenharmony_ci			if (policy == ATA_LPM_MIN_POWER)
7918c2ecf20Sopenharmony_ci				cmd |= PORT_CMD_ASP;
7928c2ecf20Sopenharmony_ci			else if (policy == ATA_LPM_MIN_POWER_WITH_PARTIAL)
7938c2ecf20Sopenharmony_ci				cmd &= ~PORT_CMD_ASP;
7948c2ecf20Sopenharmony_ci
7958c2ecf20Sopenharmony_ci			/* write out new cmd value */
7968c2ecf20Sopenharmony_ci			writel(cmd, port_mmio + PORT_CMD);
7978c2ecf20Sopenharmony_ci		}
7988c2ecf20Sopenharmony_ci	}
7998c2ecf20Sopenharmony_ci
8008c2ecf20Sopenharmony_ci	/* set aggressive device sleep */
8018c2ecf20Sopenharmony_ci	if ((hpriv->cap2 & HOST_CAP2_SDS) &&
8028c2ecf20Sopenharmony_ci	    (hpriv->cap2 & HOST_CAP2_SADM) &&
8038c2ecf20Sopenharmony_ci	    (link->device->flags & ATA_DFLAG_DEVSLP)) {
8048c2ecf20Sopenharmony_ci		if (policy == ATA_LPM_MIN_POWER ||
8058c2ecf20Sopenharmony_ci		    policy == ATA_LPM_MIN_POWER_WITH_PARTIAL)
8068c2ecf20Sopenharmony_ci			ahci_set_aggressive_devslp(ap, true);
8078c2ecf20Sopenharmony_ci		else
8088c2ecf20Sopenharmony_ci			ahci_set_aggressive_devslp(ap, false);
8098c2ecf20Sopenharmony_ci	}
8108c2ecf20Sopenharmony_ci
8118c2ecf20Sopenharmony_ci	if (policy == ATA_LPM_MAX_POWER) {
8128c2ecf20Sopenharmony_ci		sata_link_scr_lpm(link, policy, false);
8138c2ecf20Sopenharmony_ci
8148c2ecf20Sopenharmony_ci		/* turn PHYRDY IRQ back on */
8158c2ecf20Sopenharmony_ci		pp->intr_mask |= PORT_IRQ_PHYRDY;
8168c2ecf20Sopenharmony_ci		writel(pp->intr_mask, port_mmio + PORT_IRQ_MASK);
8178c2ecf20Sopenharmony_ci	}
8188c2ecf20Sopenharmony_ci
8198c2ecf20Sopenharmony_ci	return 0;
8208c2ecf20Sopenharmony_ci}
8218c2ecf20Sopenharmony_ci
8228c2ecf20Sopenharmony_ci#ifdef CONFIG_PM
8238c2ecf20Sopenharmony_cistatic void ahci_power_down(struct ata_port *ap)
8248c2ecf20Sopenharmony_ci{
8258c2ecf20Sopenharmony_ci	struct ahci_host_priv *hpriv = ap->host->private_data;
8268c2ecf20Sopenharmony_ci	void __iomem *port_mmio = ahci_port_base(ap);
8278c2ecf20Sopenharmony_ci	u32 cmd, scontrol;
8288c2ecf20Sopenharmony_ci
8298c2ecf20Sopenharmony_ci	if (!(hpriv->cap & HOST_CAP_SSS))
8308c2ecf20Sopenharmony_ci		return;
8318c2ecf20Sopenharmony_ci
8328c2ecf20Sopenharmony_ci	/* put device into listen mode, first set PxSCTL.DET to 0 */
8338c2ecf20Sopenharmony_ci	scontrol = readl(port_mmio + PORT_SCR_CTL);
8348c2ecf20Sopenharmony_ci	scontrol &= ~0xf;
8358c2ecf20Sopenharmony_ci	writel(scontrol, port_mmio + PORT_SCR_CTL);
8368c2ecf20Sopenharmony_ci
8378c2ecf20Sopenharmony_ci	/* then set PxCMD.SUD to 0 */
8388c2ecf20Sopenharmony_ci	cmd = readl(port_mmio + PORT_CMD) & ~PORT_CMD_ICC_MASK;
8398c2ecf20Sopenharmony_ci	cmd &= ~PORT_CMD_SPIN_UP;
8408c2ecf20Sopenharmony_ci	writel(cmd, port_mmio + PORT_CMD);
8418c2ecf20Sopenharmony_ci}
8428c2ecf20Sopenharmony_ci#endif
8438c2ecf20Sopenharmony_ci
8448c2ecf20Sopenharmony_cistatic void ahci_start_port(struct ata_port *ap)
8458c2ecf20Sopenharmony_ci{
8468c2ecf20Sopenharmony_ci	struct ahci_host_priv *hpriv = ap->host->private_data;
8478c2ecf20Sopenharmony_ci	struct ahci_port_priv *pp = ap->private_data;
8488c2ecf20Sopenharmony_ci	struct ata_link *link;
8498c2ecf20Sopenharmony_ci	struct ahci_em_priv *emp;
8508c2ecf20Sopenharmony_ci	ssize_t rc;
8518c2ecf20Sopenharmony_ci	int i;
8528c2ecf20Sopenharmony_ci
8538c2ecf20Sopenharmony_ci	/* enable FIS reception */
8548c2ecf20Sopenharmony_ci	ahci_start_fis_rx(ap);
8558c2ecf20Sopenharmony_ci
8568c2ecf20Sopenharmony_ci	/* enable DMA */
8578c2ecf20Sopenharmony_ci	if (!(hpriv->flags & AHCI_HFLAG_DELAY_ENGINE))
8588c2ecf20Sopenharmony_ci		hpriv->start_engine(ap);
8598c2ecf20Sopenharmony_ci
8608c2ecf20Sopenharmony_ci	/* turn on LEDs */
8618c2ecf20Sopenharmony_ci	if (ap->flags & ATA_FLAG_EM) {
8628c2ecf20Sopenharmony_ci		ata_for_each_link(link, ap, EDGE) {
8638c2ecf20Sopenharmony_ci			emp = &pp->em_priv[link->pmp];
8648c2ecf20Sopenharmony_ci
8658c2ecf20Sopenharmony_ci			/* EM Transmit bit maybe busy during init */
8668c2ecf20Sopenharmony_ci			for (i = 0; i < EM_MAX_RETRY; i++) {
8678c2ecf20Sopenharmony_ci				rc = ap->ops->transmit_led_message(ap,
8688c2ecf20Sopenharmony_ci							       emp->led_state,
8698c2ecf20Sopenharmony_ci							       4);
8708c2ecf20Sopenharmony_ci				/*
8718c2ecf20Sopenharmony_ci				 * If busy, give a breather but do not
8728c2ecf20Sopenharmony_ci				 * release EH ownership by using msleep()
8738c2ecf20Sopenharmony_ci				 * instead of ata_msleep().  EM Transmit
8748c2ecf20Sopenharmony_ci				 * bit is busy for the whole host and
8758c2ecf20Sopenharmony_ci				 * releasing ownership will cause other
8768c2ecf20Sopenharmony_ci				 * ports to fail the same way.
8778c2ecf20Sopenharmony_ci				 */
8788c2ecf20Sopenharmony_ci				if (rc == -EBUSY)
8798c2ecf20Sopenharmony_ci					msleep(1);
8808c2ecf20Sopenharmony_ci				else
8818c2ecf20Sopenharmony_ci					break;
8828c2ecf20Sopenharmony_ci			}
8838c2ecf20Sopenharmony_ci		}
8848c2ecf20Sopenharmony_ci	}
8858c2ecf20Sopenharmony_ci
8868c2ecf20Sopenharmony_ci	if (ap->flags & ATA_FLAG_SW_ACTIVITY)
8878c2ecf20Sopenharmony_ci		ata_for_each_link(link, ap, EDGE)
8888c2ecf20Sopenharmony_ci			ahci_init_sw_activity(link);
8898c2ecf20Sopenharmony_ci
8908c2ecf20Sopenharmony_ci}
8918c2ecf20Sopenharmony_ci
8928c2ecf20Sopenharmony_cistatic int ahci_deinit_port(struct ata_port *ap, const char **emsg)
8938c2ecf20Sopenharmony_ci{
8948c2ecf20Sopenharmony_ci	int rc;
8958c2ecf20Sopenharmony_ci	struct ahci_host_priv *hpriv = ap->host->private_data;
8968c2ecf20Sopenharmony_ci
8978c2ecf20Sopenharmony_ci	/* disable DMA */
8988c2ecf20Sopenharmony_ci	rc = hpriv->stop_engine(ap);
8998c2ecf20Sopenharmony_ci	if (rc) {
9008c2ecf20Sopenharmony_ci		*emsg = "failed to stop engine";
9018c2ecf20Sopenharmony_ci		return rc;
9028c2ecf20Sopenharmony_ci	}
9038c2ecf20Sopenharmony_ci
9048c2ecf20Sopenharmony_ci	/* disable FIS reception */
9058c2ecf20Sopenharmony_ci	rc = ahci_stop_fis_rx(ap);
9068c2ecf20Sopenharmony_ci	if (rc) {
9078c2ecf20Sopenharmony_ci		*emsg = "failed stop FIS RX";
9088c2ecf20Sopenharmony_ci		return rc;
9098c2ecf20Sopenharmony_ci	}
9108c2ecf20Sopenharmony_ci
9118c2ecf20Sopenharmony_ci	return 0;
9128c2ecf20Sopenharmony_ci}
9138c2ecf20Sopenharmony_ci
9148c2ecf20Sopenharmony_ciint ahci_reset_controller(struct ata_host *host)
9158c2ecf20Sopenharmony_ci{
9168c2ecf20Sopenharmony_ci	struct ahci_host_priv *hpriv = host->private_data;
9178c2ecf20Sopenharmony_ci	void __iomem *mmio = hpriv->mmio;
9188c2ecf20Sopenharmony_ci	u32 tmp;
9198c2ecf20Sopenharmony_ci
9208c2ecf20Sopenharmony_ci	/* we must be in AHCI mode, before using anything
9218c2ecf20Sopenharmony_ci	 * AHCI-specific, such as HOST_RESET.
9228c2ecf20Sopenharmony_ci	 */
9238c2ecf20Sopenharmony_ci	ahci_enable_ahci(mmio);
9248c2ecf20Sopenharmony_ci
9258c2ecf20Sopenharmony_ci	/* global controller reset */
9268c2ecf20Sopenharmony_ci	if (!ahci_skip_host_reset) {
9278c2ecf20Sopenharmony_ci		tmp = readl(mmio + HOST_CTL);
9288c2ecf20Sopenharmony_ci		if ((tmp & HOST_RESET) == 0) {
9298c2ecf20Sopenharmony_ci			writel(tmp | HOST_RESET, mmio + HOST_CTL);
9308c2ecf20Sopenharmony_ci			readl(mmio + HOST_CTL); /* flush */
9318c2ecf20Sopenharmony_ci		}
9328c2ecf20Sopenharmony_ci
9338c2ecf20Sopenharmony_ci		/*
9348c2ecf20Sopenharmony_ci		 * to perform host reset, OS should set HOST_RESET
9358c2ecf20Sopenharmony_ci		 * and poll until this bit is read to be "0".
9368c2ecf20Sopenharmony_ci		 * reset must complete within 1 second, or
9378c2ecf20Sopenharmony_ci		 * the hardware should be considered fried.
9388c2ecf20Sopenharmony_ci		 */
9398c2ecf20Sopenharmony_ci		tmp = ata_wait_register(NULL, mmio + HOST_CTL, HOST_RESET,
9408c2ecf20Sopenharmony_ci					HOST_RESET, 10, 1000);
9418c2ecf20Sopenharmony_ci
9428c2ecf20Sopenharmony_ci		if (tmp & HOST_RESET) {
9438c2ecf20Sopenharmony_ci			dev_err(host->dev, "controller reset failed (0x%x)\n",
9448c2ecf20Sopenharmony_ci				tmp);
9458c2ecf20Sopenharmony_ci			return -EIO;
9468c2ecf20Sopenharmony_ci		}
9478c2ecf20Sopenharmony_ci
9488c2ecf20Sopenharmony_ci		/* turn on AHCI mode */
9498c2ecf20Sopenharmony_ci		ahci_enable_ahci(mmio);
9508c2ecf20Sopenharmony_ci
9518c2ecf20Sopenharmony_ci		/* Some registers might be cleared on reset.  Restore
9528c2ecf20Sopenharmony_ci		 * initial values.
9538c2ecf20Sopenharmony_ci		 */
9548c2ecf20Sopenharmony_ci		if (!(hpriv->flags & AHCI_HFLAG_NO_WRITE_TO_RO))
9558c2ecf20Sopenharmony_ci			ahci_restore_initial_config(host);
9568c2ecf20Sopenharmony_ci	} else
9578c2ecf20Sopenharmony_ci		dev_info(host->dev, "skipping global host reset\n");
9588c2ecf20Sopenharmony_ci
9598c2ecf20Sopenharmony_ci	return 0;
9608c2ecf20Sopenharmony_ci}
9618c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ahci_reset_controller);
9628c2ecf20Sopenharmony_ci
9638c2ecf20Sopenharmony_cistatic void ahci_sw_activity(struct ata_link *link)
9648c2ecf20Sopenharmony_ci{
9658c2ecf20Sopenharmony_ci	struct ata_port *ap = link->ap;
9668c2ecf20Sopenharmony_ci	struct ahci_port_priv *pp = ap->private_data;
9678c2ecf20Sopenharmony_ci	struct ahci_em_priv *emp = &pp->em_priv[link->pmp];
9688c2ecf20Sopenharmony_ci
9698c2ecf20Sopenharmony_ci	if (!(link->flags & ATA_LFLAG_SW_ACTIVITY))
9708c2ecf20Sopenharmony_ci		return;
9718c2ecf20Sopenharmony_ci
9728c2ecf20Sopenharmony_ci	emp->activity++;
9738c2ecf20Sopenharmony_ci	if (!timer_pending(&emp->timer))
9748c2ecf20Sopenharmony_ci		mod_timer(&emp->timer, jiffies + msecs_to_jiffies(10));
9758c2ecf20Sopenharmony_ci}
9768c2ecf20Sopenharmony_ci
9778c2ecf20Sopenharmony_cistatic void ahci_sw_activity_blink(struct timer_list *t)
9788c2ecf20Sopenharmony_ci{
9798c2ecf20Sopenharmony_ci	struct ahci_em_priv *emp = from_timer(emp, t, timer);
9808c2ecf20Sopenharmony_ci	struct ata_link *link = emp->link;
9818c2ecf20Sopenharmony_ci	struct ata_port *ap = link->ap;
9828c2ecf20Sopenharmony_ci
9838c2ecf20Sopenharmony_ci	unsigned long led_message = emp->led_state;
9848c2ecf20Sopenharmony_ci	u32 activity_led_state;
9858c2ecf20Sopenharmony_ci	unsigned long flags;
9868c2ecf20Sopenharmony_ci
9878c2ecf20Sopenharmony_ci	led_message &= EM_MSG_LED_VALUE;
9888c2ecf20Sopenharmony_ci	led_message |= ap->port_no | (link->pmp << 8);
9898c2ecf20Sopenharmony_ci
9908c2ecf20Sopenharmony_ci	/* check to see if we've had activity.  If so,
9918c2ecf20Sopenharmony_ci	 * toggle state of LED and reset timer.  If not,
9928c2ecf20Sopenharmony_ci	 * turn LED to desired idle state.
9938c2ecf20Sopenharmony_ci	 */
9948c2ecf20Sopenharmony_ci	spin_lock_irqsave(ap->lock, flags);
9958c2ecf20Sopenharmony_ci	if (emp->saved_activity != emp->activity) {
9968c2ecf20Sopenharmony_ci		emp->saved_activity = emp->activity;
9978c2ecf20Sopenharmony_ci		/* get the current LED state */
9988c2ecf20Sopenharmony_ci		activity_led_state = led_message & EM_MSG_LED_VALUE_ON;
9998c2ecf20Sopenharmony_ci
10008c2ecf20Sopenharmony_ci		if (activity_led_state)
10018c2ecf20Sopenharmony_ci			activity_led_state = 0;
10028c2ecf20Sopenharmony_ci		else
10038c2ecf20Sopenharmony_ci			activity_led_state = 1;
10048c2ecf20Sopenharmony_ci
10058c2ecf20Sopenharmony_ci		/* clear old state */
10068c2ecf20Sopenharmony_ci		led_message &= ~EM_MSG_LED_VALUE_ACTIVITY;
10078c2ecf20Sopenharmony_ci
10088c2ecf20Sopenharmony_ci		/* toggle state */
10098c2ecf20Sopenharmony_ci		led_message |= (activity_led_state << 16);
10108c2ecf20Sopenharmony_ci		mod_timer(&emp->timer, jiffies + msecs_to_jiffies(100));
10118c2ecf20Sopenharmony_ci	} else {
10128c2ecf20Sopenharmony_ci		/* switch to idle */
10138c2ecf20Sopenharmony_ci		led_message &= ~EM_MSG_LED_VALUE_ACTIVITY;
10148c2ecf20Sopenharmony_ci		if (emp->blink_policy == BLINK_OFF)
10158c2ecf20Sopenharmony_ci			led_message |= (1 << 16);
10168c2ecf20Sopenharmony_ci	}
10178c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(ap->lock, flags);
10188c2ecf20Sopenharmony_ci	ap->ops->transmit_led_message(ap, led_message, 4);
10198c2ecf20Sopenharmony_ci}
10208c2ecf20Sopenharmony_ci
10218c2ecf20Sopenharmony_cistatic void ahci_init_sw_activity(struct ata_link *link)
10228c2ecf20Sopenharmony_ci{
10238c2ecf20Sopenharmony_ci	struct ata_port *ap = link->ap;
10248c2ecf20Sopenharmony_ci	struct ahci_port_priv *pp = ap->private_data;
10258c2ecf20Sopenharmony_ci	struct ahci_em_priv *emp = &pp->em_priv[link->pmp];
10268c2ecf20Sopenharmony_ci
10278c2ecf20Sopenharmony_ci	/* init activity stats, setup timer */
10288c2ecf20Sopenharmony_ci	emp->saved_activity = emp->activity = 0;
10298c2ecf20Sopenharmony_ci	emp->link = link;
10308c2ecf20Sopenharmony_ci	timer_setup(&emp->timer, ahci_sw_activity_blink, 0);
10318c2ecf20Sopenharmony_ci
10328c2ecf20Sopenharmony_ci	/* check our blink policy and set flag for link if it's enabled */
10338c2ecf20Sopenharmony_ci	if (emp->blink_policy)
10348c2ecf20Sopenharmony_ci		link->flags |= ATA_LFLAG_SW_ACTIVITY;
10358c2ecf20Sopenharmony_ci}
10368c2ecf20Sopenharmony_ci
10378c2ecf20Sopenharmony_ciint ahci_reset_em(struct ata_host *host)
10388c2ecf20Sopenharmony_ci{
10398c2ecf20Sopenharmony_ci	struct ahci_host_priv *hpriv = host->private_data;
10408c2ecf20Sopenharmony_ci	void __iomem *mmio = hpriv->mmio;
10418c2ecf20Sopenharmony_ci	u32 em_ctl;
10428c2ecf20Sopenharmony_ci
10438c2ecf20Sopenharmony_ci	em_ctl = readl(mmio + HOST_EM_CTL);
10448c2ecf20Sopenharmony_ci	if ((em_ctl & EM_CTL_TM) || (em_ctl & EM_CTL_RST))
10458c2ecf20Sopenharmony_ci		return -EINVAL;
10468c2ecf20Sopenharmony_ci
10478c2ecf20Sopenharmony_ci	writel(em_ctl | EM_CTL_RST, mmio + HOST_EM_CTL);
10488c2ecf20Sopenharmony_ci	return 0;
10498c2ecf20Sopenharmony_ci}
10508c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ahci_reset_em);
10518c2ecf20Sopenharmony_ci
10528c2ecf20Sopenharmony_cistatic ssize_t ahci_transmit_led_message(struct ata_port *ap, u32 state,
10538c2ecf20Sopenharmony_ci					ssize_t size)
10548c2ecf20Sopenharmony_ci{
10558c2ecf20Sopenharmony_ci	struct ahci_host_priv *hpriv = ap->host->private_data;
10568c2ecf20Sopenharmony_ci	struct ahci_port_priv *pp = ap->private_data;
10578c2ecf20Sopenharmony_ci	void __iomem *mmio = hpriv->mmio;
10588c2ecf20Sopenharmony_ci	u32 em_ctl;
10598c2ecf20Sopenharmony_ci	u32 message[] = {0, 0};
10608c2ecf20Sopenharmony_ci	unsigned long flags;
10618c2ecf20Sopenharmony_ci	int pmp;
10628c2ecf20Sopenharmony_ci	struct ahci_em_priv *emp;
10638c2ecf20Sopenharmony_ci
10648c2ecf20Sopenharmony_ci	/* get the slot number from the message */
10658c2ecf20Sopenharmony_ci	pmp = (state & EM_MSG_LED_PMP_SLOT) >> 8;
10668c2ecf20Sopenharmony_ci	if (pmp < EM_MAX_SLOTS)
10678c2ecf20Sopenharmony_ci		emp = &pp->em_priv[pmp];
10688c2ecf20Sopenharmony_ci	else
10698c2ecf20Sopenharmony_ci		return -EINVAL;
10708c2ecf20Sopenharmony_ci
10718c2ecf20Sopenharmony_ci	ahci_rpm_get_port(ap);
10728c2ecf20Sopenharmony_ci	spin_lock_irqsave(ap->lock, flags);
10738c2ecf20Sopenharmony_ci
10748c2ecf20Sopenharmony_ci	/*
10758c2ecf20Sopenharmony_ci	 * if we are still busy transmitting a previous message,
10768c2ecf20Sopenharmony_ci	 * do not allow
10778c2ecf20Sopenharmony_ci	 */
10788c2ecf20Sopenharmony_ci	em_ctl = readl(mmio + HOST_EM_CTL);
10798c2ecf20Sopenharmony_ci	if (em_ctl & EM_CTL_TM) {
10808c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(ap->lock, flags);
10818c2ecf20Sopenharmony_ci		ahci_rpm_put_port(ap);
10828c2ecf20Sopenharmony_ci		return -EBUSY;
10838c2ecf20Sopenharmony_ci	}
10848c2ecf20Sopenharmony_ci
10858c2ecf20Sopenharmony_ci	if (hpriv->em_msg_type & EM_MSG_TYPE_LED) {
10868c2ecf20Sopenharmony_ci		/*
10878c2ecf20Sopenharmony_ci		 * create message header - this is all zero except for
10888c2ecf20Sopenharmony_ci		 * the message size, which is 4 bytes.
10898c2ecf20Sopenharmony_ci		 */
10908c2ecf20Sopenharmony_ci		message[0] |= (4 << 8);
10918c2ecf20Sopenharmony_ci
10928c2ecf20Sopenharmony_ci		/* ignore 0:4 of byte zero, fill in port info yourself */
10938c2ecf20Sopenharmony_ci		message[1] = ((state & ~EM_MSG_LED_HBA_PORT) | ap->port_no);
10948c2ecf20Sopenharmony_ci
10958c2ecf20Sopenharmony_ci		/* write message to EM_LOC */
10968c2ecf20Sopenharmony_ci		writel(message[0], mmio + hpriv->em_loc);
10978c2ecf20Sopenharmony_ci		writel(message[1], mmio + hpriv->em_loc+4);
10988c2ecf20Sopenharmony_ci
10998c2ecf20Sopenharmony_ci		/*
11008c2ecf20Sopenharmony_ci		 * tell hardware to transmit the message
11018c2ecf20Sopenharmony_ci		 */
11028c2ecf20Sopenharmony_ci		writel(em_ctl | EM_CTL_TM, mmio + HOST_EM_CTL);
11038c2ecf20Sopenharmony_ci	}
11048c2ecf20Sopenharmony_ci
11058c2ecf20Sopenharmony_ci	/* save off new led state for port/slot */
11068c2ecf20Sopenharmony_ci	emp->led_state = state;
11078c2ecf20Sopenharmony_ci
11088c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(ap->lock, flags);
11098c2ecf20Sopenharmony_ci	ahci_rpm_put_port(ap);
11108c2ecf20Sopenharmony_ci
11118c2ecf20Sopenharmony_ci	return size;
11128c2ecf20Sopenharmony_ci}
11138c2ecf20Sopenharmony_ci
11148c2ecf20Sopenharmony_cistatic ssize_t ahci_led_show(struct ata_port *ap, char *buf)
11158c2ecf20Sopenharmony_ci{
11168c2ecf20Sopenharmony_ci	struct ahci_port_priv *pp = ap->private_data;
11178c2ecf20Sopenharmony_ci	struct ata_link *link;
11188c2ecf20Sopenharmony_ci	struct ahci_em_priv *emp;
11198c2ecf20Sopenharmony_ci	int rc = 0;
11208c2ecf20Sopenharmony_ci
11218c2ecf20Sopenharmony_ci	ata_for_each_link(link, ap, EDGE) {
11228c2ecf20Sopenharmony_ci		emp = &pp->em_priv[link->pmp];
11238c2ecf20Sopenharmony_ci		rc += sprintf(buf, "%lx\n", emp->led_state);
11248c2ecf20Sopenharmony_ci	}
11258c2ecf20Sopenharmony_ci	return rc;
11268c2ecf20Sopenharmony_ci}
11278c2ecf20Sopenharmony_ci
11288c2ecf20Sopenharmony_cistatic ssize_t ahci_led_store(struct ata_port *ap, const char *buf,
11298c2ecf20Sopenharmony_ci				size_t size)
11308c2ecf20Sopenharmony_ci{
11318c2ecf20Sopenharmony_ci	unsigned int state;
11328c2ecf20Sopenharmony_ci	int pmp;
11338c2ecf20Sopenharmony_ci	struct ahci_port_priv *pp = ap->private_data;
11348c2ecf20Sopenharmony_ci	struct ahci_em_priv *emp;
11358c2ecf20Sopenharmony_ci
11368c2ecf20Sopenharmony_ci	if (kstrtouint(buf, 0, &state) < 0)
11378c2ecf20Sopenharmony_ci		return -EINVAL;
11388c2ecf20Sopenharmony_ci
11398c2ecf20Sopenharmony_ci	/* get the slot number from the message */
11408c2ecf20Sopenharmony_ci	pmp = (state & EM_MSG_LED_PMP_SLOT) >> 8;
11418c2ecf20Sopenharmony_ci	if (pmp < EM_MAX_SLOTS) {
11428c2ecf20Sopenharmony_ci		pmp = array_index_nospec(pmp, EM_MAX_SLOTS);
11438c2ecf20Sopenharmony_ci		emp = &pp->em_priv[pmp];
11448c2ecf20Sopenharmony_ci	} else {
11458c2ecf20Sopenharmony_ci		return -EINVAL;
11468c2ecf20Sopenharmony_ci	}
11478c2ecf20Sopenharmony_ci
11488c2ecf20Sopenharmony_ci	/* mask off the activity bits if we are in sw_activity
11498c2ecf20Sopenharmony_ci	 * mode, user should turn off sw_activity before setting
11508c2ecf20Sopenharmony_ci	 * activity led through em_message
11518c2ecf20Sopenharmony_ci	 */
11528c2ecf20Sopenharmony_ci	if (emp->blink_policy)
11538c2ecf20Sopenharmony_ci		state &= ~EM_MSG_LED_VALUE_ACTIVITY;
11548c2ecf20Sopenharmony_ci
11558c2ecf20Sopenharmony_ci	return ap->ops->transmit_led_message(ap, state, size);
11568c2ecf20Sopenharmony_ci}
11578c2ecf20Sopenharmony_ci
11588c2ecf20Sopenharmony_cistatic ssize_t ahci_activity_store(struct ata_device *dev, enum sw_activity val)
11598c2ecf20Sopenharmony_ci{
11608c2ecf20Sopenharmony_ci	struct ata_link *link = dev->link;
11618c2ecf20Sopenharmony_ci	struct ata_port *ap = link->ap;
11628c2ecf20Sopenharmony_ci	struct ahci_port_priv *pp = ap->private_data;
11638c2ecf20Sopenharmony_ci	struct ahci_em_priv *emp = &pp->em_priv[link->pmp];
11648c2ecf20Sopenharmony_ci	u32 port_led_state = emp->led_state;
11658c2ecf20Sopenharmony_ci
11668c2ecf20Sopenharmony_ci	/* save the desired Activity LED behavior */
11678c2ecf20Sopenharmony_ci	if (val == OFF) {
11688c2ecf20Sopenharmony_ci		/* clear LFLAG */
11698c2ecf20Sopenharmony_ci		link->flags &= ~(ATA_LFLAG_SW_ACTIVITY);
11708c2ecf20Sopenharmony_ci
11718c2ecf20Sopenharmony_ci		/* set the LED to OFF */
11728c2ecf20Sopenharmony_ci		port_led_state &= EM_MSG_LED_VALUE_OFF;
11738c2ecf20Sopenharmony_ci		port_led_state |= (ap->port_no | (link->pmp << 8));
11748c2ecf20Sopenharmony_ci		ap->ops->transmit_led_message(ap, port_led_state, 4);
11758c2ecf20Sopenharmony_ci	} else {
11768c2ecf20Sopenharmony_ci		link->flags |= ATA_LFLAG_SW_ACTIVITY;
11778c2ecf20Sopenharmony_ci		if (val == BLINK_OFF) {
11788c2ecf20Sopenharmony_ci			/* set LED to ON for idle */
11798c2ecf20Sopenharmony_ci			port_led_state &= EM_MSG_LED_VALUE_OFF;
11808c2ecf20Sopenharmony_ci			port_led_state |= (ap->port_no | (link->pmp << 8));
11818c2ecf20Sopenharmony_ci			port_led_state |= EM_MSG_LED_VALUE_ON; /* check this */
11828c2ecf20Sopenharmony_ci			ap->ops->transmit_led_message(ap, port_led_state, 4);
11838c2ecf20Sopenharmony_ci		}
11848c2ecf20Sopenharmony_ci	}
11858c2ecf20Sopenharmony_ci	emp->blink_policy = val;
11868c2ecf20Sopenharmony_ci	return 0;
11878c2ecf20Sopenharmony_ci}
11888c2ecf20Sopenharmony_ci
11898c2ecf20Sopenharmony_cistatic ssize_t ahci_activity_show(struct ata_device *dev, char *buf)
11908c2ecf20Sopenharmony_ci{
11918c2ecf20Sopenharmony_ci	struct ata_link *link = dev->link;
11928c2ecf20Sopenharmony_ci	struct ata_port *ap = link->ap;
11938c2ecf20Sopenharmony_ci	struct ahci_port_priv *pp = ap->private_data;
11948c2ecf20Sopenharmony_ci	struct ahci_em_priv *emp = &pp->em_priv[link->pmp];
11958c2ecf20Sopenharmony_ci
11968c2ecf20Sopenharmony_ci	/* display the saved value of activity behavior for this
11978c2ecf20Sopenharmony_ci	 * disk.
11988c2ecf20Sopenharmony_ci	 */
11998c2ecf20Sopenharmony_ci	return sprintf(buf, "%d\n", emp->blink_policy);
12008c2ecf20Sopenharmony_ci}
12018c2ecf20Sopenharmony_ci
12028c2ecf20Sopenharmony_cistatic void ahci_port_clear_pending_irq(struct ata_port *ap)
12038c2ecf20Sopenharmony_ci{
12048c2ecf20Sopenharmony_ci	struct ahci_host_priv *hpriv = ap->host->private_data;
12058c2ecf20Sopenharmony_ci	void __iomem *port_mmio = ahci_port_base(ap);
12068c2ecf20Sopenharmony_ci	u32 tmp;
12078c2ecf20Sopenharmony_ci
12088c2ecf20Sopenharmony_ci	/* clear SError */
12098c2ecf20Sopenharmony_ci	tmp = readl(port_mmio + PORT_SCR_ERR);
12108c2ecf20Sopenharmony_ci	dev_dbg(ap->host->dev, "PORT_SCR_ERR 0x%x\n", tmp);
12118c2ecf20Sopenharmony_ci	writel(tmp, port_mmio + PORT_SCR_ERR);
12128c2ecf20Sopenharmony_ci
12138c2ecf20Sopenharmony_ci	/* clear port IRQ */
12148c2ecf20Sopenharmony_ci	tmp = readl(port_mmio + PORT_IRQ_STAT);
12158c2ecf20Sopenharmony_ci	dev_dbg(ap->host->dev, "PORT_IRQ_STAT 0x%x\n", tmp);
12168c2ecf20Sopenharmony_ci	if (tmp)
12178c2ecf20Sopenharmony_ci		writel(tmp, port_mmio + PORT_IRQ_STAT);
12188c2ecf20Sopenharmony_ci
12198c2ecf20Sopenharmony_ci	writel(1 << ap->port_no, hpriv->mmio + HOST_IRQ_STAT);
12208c2ecf20Sopenharmony_ci}
12218c2ecf20Sopenharmony_ci
12228c2ecf20Sopenharmony_cistatic void ahci_port_init(struct device *dev, struct ata_port *ap,
12238c2ecf20Sopenharmony_ci			   int port_no, void __iomem *mmio,
12248c2ecf20Sopenharmony_ci			   void __iomem *port_mmio)
12258c2ecf20Sopenharmony_ci{
12268c2ecf20Sopenharmony_ci	struct ahci_host_priv *hpriv = ap->host->private_data;
12278c2ecf20Sopenharmony_ci	const char *emsg = NULL;
12288c2ecf20Sopenharmony_ci	int rc;
12298c2ecf20Sopenharmony_ci	u32 tmp;
12308c2ecf20Sopenharmony_ci
12318c2ecf20Sopenharmony_ci	/* make sure port is not active */
12328c2ecf20Sopenharmony_ci	rc = ahci_deinit_port(ap, &emsg);
12338c2ecf20Sopenharmony_ci	if (rc)
12348c2ecf20Sopenharmony_ci		dev_warn(dev, "%s (%d)\n", emsg, rc);
12358c2ecf20Sopenharmony_ci
12368c2ecf20Sopenharmony_ci	ahci_port_clear_pending_irq(ap);
12378c2ecf20Sopenharmony_ci
12388c2ecf20Sopenharmony_ci	/* mark esata ports */
12398c2ecf20Sopenharmony_ci	tmp = readl(port_mmio + PORT_CMD);
12408c2ecf20Sopenharmony_ci	if ((tmp & PORT_CMD_ESP) && (hpriv->cap & HOST_CAP_SXS))
12418c2ecf20Sopenharmony_ci		ap->pflags |= ATA_PFLAG_EXTERNAL;
12428c2ecf20Sopenharmony_ci}
12438c2ecf20Sopenharmony_ci
12448c2ecf20Sopenharmony_civoid ahci_init_controller(struct ata_host *host)
12458c2ecf20Sopenharmony_ci{
12468c2ecf20Sopenharmony_ci	struct ahci_host_priv *hpriv = host->private_data;
12478c2ecf20Sopenharmony_ci	void __iomem *mmio = hpriv->mmio;
12488c2ecf20Sopenharmony_ci	int i;
12498c2ecf20Sopenharmony_ci	void __iomem *port_mmio;
12508c2ecf20Sopenharmony_ci	u32 tmp;
12518c2ecf20Sopenharmony_ci
12528c2ecf20Sopenharmony_ci	for (i = 0; i < host->n_ports; i++) {
12538c2ecf20Sopenharmony_ci		struct ata_port *ap = host->ports[i];
12548c2ecf20Sopenharmony_ci
12558c2ecf20Sopenharmony_ci		port_mmio = ahci_port_base(ap);
12568c2ecf20Sopenharmony_ci		if (ata_port_is_dummy(ap))
12578c2ecf20Sopenharmony_ci			continue;
12588c2ecf20Sopenharmony_ci
12598c2ecf20Sopenharmony_ci		ahci_port_init(host->dev, ap, i, mmio, port_mmio);
12608c2ecf20Sopenharmony_ci	}
12618c2ecf20Sopenharmony_ci
12628c2ecf20Sopenharmony_ci	tmp = readl(mmio + HOST_CTL);
12638c2ecf20Sopenharmony_ci	dev_dbg(host->dev, "HOST_CTL 0x%x\n", tmp);
12648c2ecf20Sopenharmony_ci	writel(tmp | HOST_IRQ_EN, mmio + HOST_CTL);
12658c2ecf20Sopenharmony_ci	tmp = readl(mmio + HOST_CTL);
12668c2ecf20Sopenharmony_ci	dev_dbg(host->dev, "HOST_CTL 0x%x\n", tmp);
12678c2ecf20Sopenharmony_ci}
12688c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ahci_init_controller);
12698c2ecf20Sopenharmony_ci
12708c2ecf20Sopenharmony_cistatic void ahci_dev_config(struct ata_device *dev)
12718c2ecf20Sopenharmony_ci{
12728c2ecf20Sopenharmony_ci	struct ahci_host_priv *hpriv = dev->link->ap->host->private_data;
12738c2ecf20Sopenharmony_ci
12748c2ecf20Sopenharmony_ci	if (hpriv->flags & AHCI_HFLAG_SECT255) {
12758c2ecf20Sopenharmony_ci		dev->max_sectors = 255;
12768c2ecf20Sopenharmony_ci		ata_dev_info(dev,
12778c2ecf20Sopenharmony_ci			     "SB600 AHCI: limiting to 255 sectors per cmd\n");
12788c2ecf20Sopenharmony_ci	}
12798c2ecf20Sopenharmony_ci}
12808c2ecf20Sopenharmony_ci
12818c2ecf20Sopenharmony_ciunsigned int ahci_dev_classify(struct ata_port *ap)
12828c2ecf20Sopenharmony_ci{
12838c2ecf20Sopenharmony_ci	void __iomem *port_mmio = ahci_port_base(ap);
12848c2ecf20Sopenharmony_ci	struct ata_taskfile tf;
12858c2ecf20Sopenharmony_ci	u32 tmp;
12868c2ecf20Sopenharmony_ci
12878c2ecf20Sopenharmony_ci	tmp = readl(port_mmio + PORT_SIG);
12888c2ecf20Sopenharmony_ci	tf.lbah		= (tmp >> 24)	& 0xff;
12898c2ecf20Sopenharmony_ci	tf.lbam		= (tmp >> 16)	& 0xff;
12908c2ecf20Sopenharmony_ci	tf.lbal		= (tmp >> 8)	& 0xff;
12918c2ecf20Sopenharmony_ci	tf.nsect	= (tmp)		& 0xff;
12928c2ecf20Sopenharmony_ci
12938c2ecf20Sopenharmony_ci	return ata_dev_classify(&tf);
12948c2ecf20Sopenharmony_ci}
12958c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ahci_dev_classify);
12968c2ecf20Sopenharmony_ci
12978c2ecf20Sopenharmony_civoid ahci_fill_cmd_slot(struct ahci_port_priv *pp, unsigned int tag,
12988c2ecf20Sopenharmony_ci			u32 opts)
12998c2ecf20Sopenharmony_ci{
13008c2ecf20Sopenharmony_ci	dma_addr_t cmd_tbl_dma;
13018c2ecf20Sopenharmony_ci
13028c2ecf20Sopenharmony_ci	cmd_tbl_dma = pp->cmd_tbl_dma + tag * AHCI_CMD_TBL_SZ;
13038c2ecf20Sopenharmony_ci
13048c2ecf20Sopenharmony_ci	pp->cmd_slot[tag].opts = cpu_to_le32(opts);
13058c2ecf20Sopenharmony_ci	pp->cmd_slot[tag].status = 0;
13068c2ecf20Sopenharmony_ci	pp->cmd_slot[tag].tbl_addr = cpu_to_le32(cmd_tbl_dma & 0xffffffff);
13078c2ecf20Sopenharmony_ci	pp->cmd_slot[tag].tbl_addr_hi = cpu_to_le32((cmd_tbl_dma >> 16) >> 16);
13088c2ecf20Sopenharmony_ci}
13098c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ahci_fill_cmd_slot);
13108c2ecf20Sopenharmony_ci
13118c2ecf20Sopenharmony_ciint ahci_kick_engine(struct ata_port *ap)
13128c2ecf20Sopenharmony_ci{
13138c2ecf20Sopenharmony_ci	void __iomem *port_mmio = ahci_port_base(ap);
13148c2ecf20Sopenharmony_ci	struct ahci_host_priv *hpriv = ap->host->private_data;
13158c2ecf20Sopenharmony_ci	u8 status = readl(port_mmio + PORT_TFDATA) & 0xFF;
13168c2ecf20Sopenharmony_ci	u32 tmp;
13178c2ecf20Sopenharmony_ci	int busy, rc;
13188c2ecf20Sopenharmony_ci
13198c2ecf20Sopenharmony_ci	/* stop engine */
13208c2ecf20Sopenharmony_ci	rc = hpriv->stop_engine(ap);
13218c2ecf20Sopenharmony_ci	if (rc)
13228c2ecf20Sopenharmony_ci		goto out_restart;
13238c2ecf20Sopenharmony_ci
13248c2ecf20Sopenharmony_ci	/* need to do CLO?
13258c2ecf20Sopenharmony_ci	 * always do CLO if PMP is attached (AHCI-1.3 9.2)
13268c2ecf20Sopenharmony_ci	 */
13278c2ecf20Sopenharmony_ci	busy = status & (ATA_BUSY | ATA_DRQ);
13288c2ecf20Sopenharmony_ci	if (!busy && !sata_pmp_attached(ap)) {
13298c2ecf20Sopenharmony_ci		rc = 0;
13308c2ecf20Sopenharmony_ci		goto out_restart;
13318c2ecf20Sopenharmony_ci	}
13328c2ecf20Sopenharmony_ci
13338c2ecf20Sopenharmony_ci	if (!(hpriv->cap & HOST_CAP_CLO)) {
13348c2ecf20Sopenharmony_ci		rc = -EOPNOTSUPP;
13358c2ecf20Sopenharmony_ci		goto out_restart;
13368c2ecf20Sopenharmony_ci	}
13378c2ecf20Sopenharmony_ci
13388c2ecf20Sopenharmony_ci	/* perform CLO */
13398c2ecf20Sopenharmony_ci	tmp = readl(port_mmio + PORT_CMD);
13408c2ecf20Sopenharmony_ci	tmp |= PORT_CMD_CLO;
13418c2ecf20Sopenharmony_ci	writel(tmp, port_mmio + PORT_CMD);
13428c2ecf20Sopenharmony_ci
13438c2ecf20Sopenharmony_ci	rc = 0;
13448c2ecf20Sopenharmony_ci	tmp = ata_wait_register(ap, port_mmio + PORT_CMD,
13458c2ecf20Sopenharmony_ci				PORT_CMD_CLO, PORT_CMD_CLO, 1, 500);
13468c2ecf20Sopenharmony_ci	if (tmp & PORT_CMD_CLO)
13478c2ecf20Sopenharmony_ci		rc = -EIO;
13488c2ecf20Sopenharmony_ci
13498c2ecf20Sopenharmony_ci	/* restart engine */
13508c2ecf20Sopenharmony_ci out_restart:
13518c2ecf20Sopenharmony_ci	hpriv->start_engine(ap);
13528c2ecf20Sopenharmony_ci	return rc;
13538c2ecf20Sopenharmony_ci}
13548c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ahci_kick_engine);
13558c2ecf20Sopenharmony_ci
13568c2ecf20Sopenharmony_cistatic int ahci_exec_polled_cmd(struct ata_port *ap, int pmp,
13578c2ecf20Sopenharmony_ci				struct ata_taskfile *tf, int is_cmd, u16 flags,
13588c2ecf20Sopenharmony_ci				unsigned long timeout_msec)
13598c2ecf20Sopenharmony_ci{
13608c2ecf20Sopenharmony_ci	const u32 cmd_fis_len = 5; /* five dwords */
13618c2ecf20Sopenharmony_ci	struct ahci_port_priv *pp = ap->private_data;
13628c2ecf20Sopenharmony_ci	void __iomem *port_mmio = ahci_port_base(ap);
13638c2ecf20Sopenharmony_ci	u8 *fis = pp->cmd_tbl;
13648c2ecf20Sopenharmony_ci	u32 tmp;
13658c2ecf20Sopenharmony_ci
13668c2ecf20Sopenharmony_ci	/* prep the command */
13678c2ecf20Sopenharmony_ci	ata_tf_to_fis(tf, pmp, is_cmd, fis);
13688c2ecf20Sopenharmony_ci	ahci_fill_cmd_slot(pp, 0, cmd_fis_len | flags | (pmp << 12));
13698c2ecf20Sopenharmony_ci
13708c2ecf20Sopenharmony_ci	/* set port value for softreset of Port Multiplier */
13718c2ecf20Sopenharmony_ci	if (pp->fbs_enabled && pp->fbs_last_dev != pmp) {
13728c2ecf20Sopenharmony_ci		tmp = readl(port_mmio + PORT_FBS);
13738c2ecf20Sopenharmony_ci		tmp &= ~(PORT_FBS_DEV_MASK | PORT_FBS_DEC);
13748c2ecf20Sopenharmony_ci		tmp |= pmp << PORT_FBS_DEV_OFFSET;
13758c2ecf20Sopenharmony_ci		writel(tmp, port_mmio + PORT_FBS);
13768c2ecf20Sopenharmony_ci		pp->fbs_last_dev = pmp;
13778c2ecf20Sopenharmony_ci	}
13788c2ecf20Sopenharmony_ci
13798c2ecf20Sopenharmony_ci	/* issue & wait */
13808c2ecf20Sopenharmony_ci	writel(1, port_mmio + PORT_CMD_ISSUE);
13818c2ecf20Sopenharmony_ci
13828c2ecf20Sopenharmony_ci	if (timeout_msec) {
13838c2ecf20Sopenharmony_ci		tmp = ata_wait_register(ap, port_mmio + PORT_CMD_ISSUE,
13848c2ecf20Sopenharmony_ci					0x1, 0x1, 1, timeout_msec);
13858c2ecf20Sopenharmony_ci		if (tmp & 0x1) {
13868c2ecf20Sopenharmony_ci			ahci_kick_engine(ap);
13878c2ecf20Sopenharmony_ci			return -EBUSY;
13888c2ecf20Sopenharmony_ci		}
13898c2ecf20Sopenharmony_ci	} else
13908c2ecf20Sopenharmony_ci		readl(port_mmio + PORT_CMD_ISSUE);	/* flush */
13918c2ecf20Sopenharmony_ci
13928c2ecf20Sopenharmony_ci	return 0;
13938c2ecf20Sopenharmony_ci}
13948c2ecf20Sopenharmony_ci
13958c2ecf20Sopenharmony_ciint ahci_do_softreset(struct ata_link *link, unsigned int *class,
13968c2ecf20Sopenharmony_ci		      int pmp, unsigned long deadline,
13978c2ecf20Sopenharmony_ci		      int (*check_ready)(struct ata_link *link))
13988c2ecf20Sopenharmony_ci{
13998c2ecf20Sopenharmony_ci	struct ata_port *ap = link->ap;
14008c2ecf20Sopenharmony_ci	struct ahci_host_priv *hpriv = ap->host->private_data;
14018c2ecf20Sopenharmony_ci	struct ahci_port_priv *pp = ap->private_data;
14028c2ecf20Sopenharmony_ci	const char *reason = NULL;
14038c2ecf20Sopenharmony_ci	unsigned long now, msecs;
14048c2ecf20Sopenharmony_ci	struct ata_taskfile tf;
14058c2ecf20Sopenharmony_ci	bool fbs_disabled = false;
14068c2ecf20Sopenharmony_ci	int rc;
14078c2ecf20Sopenharmony_ci
14088c2ecf20Sopenharmony_ci	DPRINTK("ENTER\n");
14098c2ecf20Sopenharmony_ci
14108c2ecf20Sopenharmony_ci	/* prepare for SRST (AHCI-1.1 10.4.1) */
14118c2ecf20Sopenharmony_ci	rc = ahci_kick_engine(ap);
14128c2ecf20Sopenharmony_ci	if (rc && rc != -EOPNOTSUPP)
14138c2ecf20Sopenharmony_ci		ata_link_warn(link, "failed to reset engine (errno=%d)\n", rc);
14148c2ecf20Sopenharmony_ci
14158c2ecf20Sopenharmony_ci	/*
14168c2ecf20Sopenharmony_ci	 * According to AHCI-1.2 9.3.9: if FBS is enable, software shall
14178c2ecf20Sopenharmony_ci	 * clear PxFBS.EN to '0' prior to issuing software reset to devices
14188c2ecf20Sopenharmony_ci	 * that is attached to port multiplier.
14198c2ecf20Sopenharmony_ci	 */
14208c2ecf20Sopenharmony_ci	if (!ata_is_host_link(link) && pp->fbs_enabled) {
14218c2ecf20Sopenharmony_ci		ahci_disable_fbs(ap);
14228c2ecf20Sopenharmony_ci		fbs_disabled = true;
14238c2ecf20Sopenharmony_ci	}
14248c2ecf20Sopenharmony_ci
14258c2ecf20Sopenharmony_ci	ata_tf_init(link->device, &tf);
14268c2ecf20Sopenharmony_ci
14278c2ecf20Sopenharmony_ci	/* issue the first H2D Register FIS */
14288c2ecf20Sopenharmony_ci	msecs = 0;
14298c2ecf20Sopenharmony_ci	now = jiffies;
14308c2ecf20Sopenharmony_ci	if (time_after(deadline, now))
14318c2ecf20Sopenharmony_ci		msecs = jiffies_to_msecs(deadline - now);
14328c2ecf20Sopenharmony_ci
14338c2ecf20Sopenharmony_ci	tf.ctl |= ATA_SRST;
14348c2ecf20Sopenharmony_ci	if (ahci_exec_polled_cmd(ap, pmp, &tf, 0,
14358c2ecf20Sopenharmony_ci				 AHCI_CMD_RESET | AHCI_CMD_CLR_BUSY, msecs)) {
14368c2ecf20Sopenharmony_ci		rc = -EIO;
14378c2ecf20Sopenharmony_ci		reason = "1st FIS failed";
14388c2ecf20Sopenharmony_ci		goto fail;
14398c2ecf20Sopenharmony_ci	}
14408c2ecf20Sopenharmony_ci
14418c2ecf20Sopenharmony_ci	/* spec says at least 5us, but be generous and sleep for 1ms */
14428c2ecf20Sopenharmony_ci	ata_msleep(ap, 1);
14438c2ecf20Sopenharmony_ci
14448c2ecf20Sopenharmony_ci	/* issue the second H2D Register FIS */
14458c2ecf20Sopenharmony_ci	tf.ctl &= ~ATA_SRST;
14468c2ecf20Sopenharmony_ci	ahci_exec_polled_cmd(ap, pmp, &tf, 0, 0, 0);
14478c2ecf20Sopenharmony_ci
14488c2ecf20Sopenharmony_ci	/* wait for link to become ready */
14498c2ecf20Sopenharmony_ci	rc = ata_wait_after_reset(link, deadline, check_ready);
14508c2ecf20Sopenharmony_ci	if (rc == -EBUSY && hpriv->flags & AHCI_HFLAG_SRST_TOUT_IS_OFFLINE) {
14518c2ecf20Sopenharmony_ci		/*
14528c2ecf20Sopenharmony_ci		 * Workaround for cases where link online status can't
14538c2ecf20Sopenharmony_ci		 * be trusted.  Treat device readiness timeout as link
14548c2ecf20Sopenharmony_ci		 * offline.
14558c2ecf20Sopenharmony_ci		 */
14568c2ecf20Sopenharmony_ci		ata_link_info(link, "device not ready, treating as offline\n");
14578c2ecf20Sopenharmony_ci		*class = ATA_DEV_NONE;
14588c2ecf20Sopenharmony_ci	} else if (rc) {
14598c2ecf20Sopenharmony_ci		/* link occupied, -ENODEV too is an error */
14608c2ecf20Sopenharmony_ci		reason = "device not ready";
14618c2ecf20Sopenharmony_ci		goto fail;
14628c2ecf20Sopenharmony_ci	} else
14638c2ecf20Sopenharmony_ci		*class = ahci_dev_classify(ap);
14648c2ecf20Sopenharmony_ci
14658c2ecf20Sopenharmony_ci	/* re-enable FBS if disabled before */
14668c2ecf20Sopenharmony_ci	if (fbs_disabled)
14678c2ecf20Sopenharmony_ci		ahci_enable_fbs(ap);
14688c2ecf20Sopenharmony_ci
14698c2ecf20Sopenharmony_ci	DPRINTK("EXIT, class=%u\n", *class);
14708c2ecf20Sopenharmony_ci	return 0;
14718c2ecf20Sopenharmony_ci
14728c2ecf20Sopenharmony_ci fail:
14738c2ecf20Sopenharmony_ci	ata_link_err(link, "softreset failed (%s)\n", reason);
14748c2ecf20Sopenharmony_ci	return rc;
14758c2ecf20Sopenharmony_ci}
14768c2ecf20Sopenharmony_ci
14778c2ecf20Sopenharmony_ciint ahci_check_ready(struct ata_link *link)
14788c2ecf20Sopenharmony_ci{
14798c2ecf20Sopenharmony_ci	void __iomem *port_mmio = ahci_port_base(link->ap);
14808c2ecf20Sopenharmony_ci	u8 status = readl(port_mmio + PORT_TFDATA) & 0xFF;
14818c2ecf20Sopenharmony_ci
14828c2ecf20Sopenharmony_ci	return ata_check_ready(status);
14838c2ecf20Sopenharmony_ci}
14848c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ahci_check_ready);
14858c2ecf20Sopenharmony_ci
14868c2ecf20Sopenharmony_cistatic int ahci_softreset(struct ata_link *link, unsigned int *class,
14878c2ecf20Sopenharmony_ci			  unsigned long deadline)
14888c2ecf20Sopenharmony_ci{
14898c2ecf20Sopenharmony_ci	int pmp = sata_srst_pmp(link);
14908c2ecf20Sopenharmony_ci
14918c2ecf20Sopenharmony_ci	DPRINTK("ENTER\n");
14928c2ecf20Sopenharmony_ci
14938c2ecf20Sopenharmony_ci	return ahci_do_softreset(link, class, pmp, deadline, ahci_check_ready);
14948c2ecf20Sopenharmony_ci}
14958c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ahci_do_softreset);
14968c2ecf20Sopenharmony_ci
14978c2ecf20Sopenharmony_cistatic int ahci_bad_pmp_check_ready(struct ata_link *link)
14988c2ecf20Sopenharmony_ci{
14998c2ecf20Sopenharmony_ci	void __iomem *port_mmio = ahci_port_base(link->ap);
15008c2ecf20Sopenharmony_ci	u8 status = readl(port_mmio + PORT_TFDATA) & 0xFF;
15018c2ecf20Sopenharmony_ci	u32 irq_status = readl(port_mmio + PORT_IRQ_STAT);
15028c2ecf20Sopenharmony_ci
15038c2ecf20Sopenharmony_ci	/*
15048c2ecf20Sopenharmony_ci	 * There is no need to check TFDATA if BAD PMP is found due to HW bug,
15058c2ecf20Sopenharmony_ci	 * which can save timeout delay.
15068c2ecf20Sopenharmony_ci	 */
15078c2ecf20Sopenharmony_ci	if (irq_status & PORT_IRQ_BAD_PMP)
15088c2ecf20Sopenharmony_ci		return -EIO;
15098c2ecf20Sopenharmony_ci
15108c2ecf20Sopenharmony_ci	return ata_check_ready(status);
15118c2ecf20Sopenharmony_ci}
15128c2ecf20Sopenharmony_ci
15138c2ecf20Sopenharmony_cistatic int ahci_pmp_retry_softreset(struct ata_link *link, unsigned int *class,
15148c2ecf20Sopenharmony_ci				    unsigned long deadline)
15158c2ecf20Sopenharmony_ci{
15168c2ecf20Sopenharmony_ci	struct ata_port *ap = link->ap;
15178c2ecf20Sopenharmony_ci	void __iomem *port_mmio = ahci_port_base(ap);
15188c2ecf20Sopenharmony_ci	int pmp = sata_srst_pmp(link);
15198c2ecf20Sopenharmony_ci	int rc;
15208c2ecf20Sopenharmony_ci	u32 irq_sts;
15218c2ecf20Sopenharmony_ci
15228c2ecf20Sopenharmony_ci	DPRINTK("ENTER\n");
15238c2ecf20Sopenharmony_ci
15248c2ecf20Sopenharmony_ci	rc = ahci_do_softreset(link, class, pmp, deadline,
15258c2ecf20Sopenharmony_ci			       ahci_bad_pmp_check_ready);
15268c2ecf20Sopenharmony_ci
15278c2ecf20Sopenharmony_ci	/*
15288c2ecf20Sopenharmony_ci	 * Soft reset fails with IPMS set when PMP is enabled but
15298c2ecf20Sopenharmony_ci	 * SATA HDD/ODD is connected to SATA port, do soft reset
15308c2ecf20Sopenharmony_ci	 * again to port 0.
15318c2ecf20Sopenharmony_ci	 */
15328c2ecf20Sopenharmony_ci	if (rc == -EIO) {
15338c2ecf20Sopenharmony_ci		irq_sts = readl(port_mmio + PORT_IRQ_STAT);
15348c2ecf20Sopenharmony_ci		if (irq_sts & PORT_IRQ_BAD_PMP) {
15358c2ecf20Sopenharmony_ci			ata_link_warn(link,
15368c2ecf20Sopenharmony_ci					"applying PMP SRST workaround "
15378c2ecf20Sopenharmony_ci					"and retrying\n");
15388c2ecf20Sopenharmony_ci			rc = ahci_do_softreset(link, class, 0, deadline,
15398c2ecf20Sopenharmony_ci					       ahci_check_ready);
15408c2ecf20Sopenharmony_ci		}
15418c2ecf20Sopenharmony_ci	}
15428c2ecf20Sopenharmony_ci
15438c2ecf20Sopenharmony_ci	return rc;
15448c2ecf20Sopenharmony_ci}
15458c2ecf20Sopenharmony_ci
15468c2ecf20Sopenharmony_ciint ahci_do_hardreset(struct ata_link *link, unsigned int *class,
15478c2ecf20Sopenharmony_ci		      unsigned long deadline, bool *online)
15488c2ecf20Sopenharmony_ci{
15498c2ecf20Sopenharmony_ci	const unsigned long *timing = sata_ehc_deb_timing(&link->eh_context);
15508c2ecf20Sopenharmony_ci	struct ata_port *ap = link->ap;
15518c2ecf20Sopenharmony_ci	struct ahci_port_priv *pp = ap->private_data;
15528c2ecf20Sopenharmony_ci	struct ahci_host_priv *hpriv = ap->host->private_data;
15538c2ecf20Sopenharmony_ci	u8 *d2h_fis = pp->rx_fis + RX_FIS_D2H_REG;
15548c2ecf20Sopenharmony_ci	struct ata_taskfile tf;
15558c2ecf20Sopenharmony_ci	int rc;
15568c2ecf20Sopenharmony_ci
15578c2ecf20Sopenharmony_ci	DPRINTK("ENTER\n");
15588c2ecf20Sopenharmony_ci
15598c2ecf20Sopenharmony_ci	hpriv->stop_engine(ap);
15608c2ecf20Sopenharmony_ci
15618c2ecf20Sopenharmony_ci	/* clear D2H reception area to properly wait for D2H FIS */
15628c2ecf20Sopenharmony_ci	ata_tf_init(link->device, &tf);
15638c2ecf20Sopenharmony_ci	tf.command = ATA_BUSY;
15648c2ecf20Sopenharmony_ci	ata_tf_to_fis(&tf, 0, 0, d2h_fis);
15658c2ecf20Sopenharmony_ci
15668c2ecf20Sopenharmony_ci	ahci_port_clear_pending_irq(ap);
15678c2ecf20Sopenharmony_ci
15688c2ecf20Sopenharmony_ci	rc = sata_link_hardreset(link, timing, deadline, online,
15698c2ecf20Sopenharmony_ci				 ahci_check_ready);
15708c2ecf20Sopenharmony_ci
15718c2ecf20Sopenharmony_ci	hpriv->start_engine(ap);
15728c2ecf20Sopenharmony_ci
15738c2ecf20Sopenharmony_ci	if (*online)
15748c2ecf20Sopenharmony_ci		*class = ahci_dev_classify(ap);
15758c2ecf20Sopenharmony_ci
15768c2ecf20Sopenharmony_ci	DPRINTK("EXIT, rc=%d, class=%u\n", rc, *class);
15778c2ecf20Sopenharmony_ci	return rc;
15788c2ecf20Sopenharmony_ci}
15798c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ahci_do_hardreset);
15808c2ecf20Sopenharmony_ci
15818c2ecf20Sopenharmony_cistatic int ahci_hardreset(struct ata_link *link, unsigned int *class,
15828c2ecf20Sopenharmony_ci			  unsigned long deadline)
15838c2ecf20Sopenharmony_ci{
15848c2ecf20Sopenharmony_ci	bool online;
15858c2ecf20Sopenharmony_ci
15868c2ecf20Sopenharmony_ci	return ahci_do_hardreset(link, class, deadline, &online);
15878c2ecf20Sopenharmony_ci}
15888c2ecf20Sopenharmony_ci
15898c2ecf20Sopenharmony_cistatic void ahci_postreset(struct ata_link *link, unsigned int *class)
15908c2ecf20Sopenharmony_ci{
15918c2ecf20Sopenharmony_ci	struct ata_port *ap = link->ap;
15928c2ecf20Sopenharmony_ci	void __iomem *port_mmio = ahci_port_base(ap);
15938c2ecf20Sopenharmony_ci	u32 new_tmp, tmp;
15948c2ecf20Sopenharmony_ci
15958c2ecf20Sopenharmony_ci	ata_std_postreset(link, class);
15968c2ecf20Sopenharmony_ci
15978c2ecf20Sopenharmony_ci	/* Make sure port's ATAPI bit is set appropriately */
15988c2ecf20Sopenharmony_ci	new_tmp = tmp = readl(port_mmio + PORT_CMD);
15998c2ecf20Sopenharmony_ci	if (*class == ATA_DEV_ATAPI)
16008c2ecf20Sopenharmony_ci		new_tmp |= PORT_CMD_ATAPI;
16018c2ecf20Sopenharmony_ci	else
16028c2ecf20Sopenharmony_ci		new_tmp &= ~PORT_CMD_ATAPI;
16038c2ecf20Sopenharmony_ci	if (new_tmp != tmp) {
16048c2ecf20Sopenharmony_ci		writel(new_tmp, port_mmio + PORT_CMD);
16058c2ecf20Sopenharmony_ci		readl(port_mmio + PORT_CMD); /* flush */
16068c2ecf20Sopenharmony_ci	}
16078c2ecf20Sopenharmony_ci}
16088c2ecf20Sopenharmony_ci
16098c2ecf20Sopenharmony_cistatic unsigned int ahci_fill_sg(struct ata_queued_cmd *qc, void *cmd_tbl)
16108c2ecf20Sopenharmony_ci{
16118c2ecf20Sopenharmony_ci	struct scatterlist *sg;
16128c2ecf20Sopenharmony_ci	struct ahci_sg *ahci_sg = cmd_tbl + AHCI_CMD_TBL_HDR_SZ;
16138c2ecf20Sopenharmony_ci	unsigned int si;
16148c2ecf20Sopenharmony_ci
16158c2ecf20Sopenharmony_ci	VPRINTK("ENTER\n");
16168c2ecf20Sopenharmony_ci
16178c2ecf20Sopenharmony_ci	/*
16188c2ecf20Sopenharmony_ci	 * Next, the S/G list.
16198c2ecf20Sopenharmony_ci	 */
16208c2ecf20Sopenharmony_ci	for_each_sg(qc->sg, sg, qc->n_elem, si) {
16218c2ecf20Sopenharmony_ci		dma_addr_t addr = sg_dma_address(sg);
16228c2ecf20Sopenharmony_ci		u32 sg_len = sg_dma_len(sg);
16238c2ecf20Sopenharmony_ci
16248c2ecf20Sopenharmony_ci		ahci_sg[si].addr = cpu_to_le32(addr & 0xffffffff);
16258c2ecf20Sopenharmony_ci		ahci_sg[si].addr_hi = cpu_to_le32((addr >> 16) >> 16);
16268c2ecf20Sopenharmony_ci		ahci_sg[si].flags_size = cpu_to_le32(sg_len - 1);
16278c2ecf20Sopenharmony_ci	}
16288c2ecf20Sopenharmony_ci
16298c2ecf20Sopenharmony_ci	return si;
16308c2ecf20Sopenharmony_ci}
16318c2ecf20Sopenharmony_ci
16328c2ecf20Sopenharmony_cistatic int ahci_pmp_qc_defer(struct ata_queued_cmd *qc)
16338c2ecf20Sopenharmony_ci{
16348c2ecf20Sopenharmony_ci	struct ata_port *ap = qc->ap;
16358c2ecf20Sopenharmony_ci	struct ahci_port_priv *pp = ap->private_data;
16368c2ecf20Sopenharmony_ci
16378c2ecf20Sopenharmony_ci	if (!sata_pmp_attached(ap) || pp->fbs_enabled)
16388c2ecf20Sopenharmony_ci		return ata_std_qc_defer(qc);
16398c2ecf20Sopenharmony_ci	else
16408c2ecf20Sopenharmony_ci		return sata_pmp_qc_defer_cmd_switch(qc);
16418c2ecf20Sopenharmony_ci}
16428c2ecf20Sopenharmony_ci
16438c2ecf20Sopenharmony_cistatic enum ata_completion_errors ahci_qc_prep(struct ata_queued_cmd *qc)
16448c2ecf20Sopenharmony_ci{
16458c2ecf20Sopenharmony_ci	struct ata_port *ap = qc->ap;
16468c2ecf20Sopenharmony_ci	struct ahci_port_priv *pp = ap->private_data;
16478c2ecf20Sopenharmony_ci	int is_atapi = ata_is_atapi(qc->tf.protocol);
16488c2ecf20Sopenharmony_ci	void *cmd_tbl;
16498c2ecf20Sopenharmony_ci	u32 opts;
16508c2ecf20Sopenharmony_ci	const u32 cmd_fis_len = 5; /* five dwords */
16518c2ecf20Sopenharmony_ci	unsigned int n_elem;
16528c2ecf20Sopenharmony_ci
16538c2ecf20Sopenharmony_ci	/*
16548c2ecf20Sopenharmony_ci	 * Fill in command table information.  First, the header,
16558c2ecf20Sopenharmony_ci	 * a SATA Register - Host to Device command FIS.
16568c2ecf20Sopenharmony_ci	 */
16578c2ecf20Sopenharmony_ci	cmd_tbl = pp->cmd_tbl + qc->hw_tag * AHCI_CMD_TBL_SZ;
16588c2ecf20Sopenharmony_ci
16598c2ecf20Sopenharmony_ci	ata_tf_to_fis(&qc->tf, qc->dev->link->pmp, 1, cmd_tbl);
16608c2ecf20Sopenharmony_ci	if (is_atapi) {
16618c2ecf20Sopenharmony_ci		memset(cmd_tbl + AHCI_CMD_TBL_CDB, 0, 32);
16628c2ecf20Sopenharmony_ci		memcpy(cmd_tbl + AHCI_CMD_TBL_CDB, qc->cdb, qc->dev->cdb_len);
16638c2ecf20Sopenharmony_ci	}
16648c2ecf20Sopenharmony_ci
16658c2ecf20Sopenharmony_ci	n_elem = 0;
16668c2ecf20Sopenharmony_ci	if (qc->flags & ATA_QCFLAG_DMAMAP)
16678c2ecf20Sopenharmony_ci		n_elem = ahci_fill_sg(qc, cmd_tbl);
16688c2ecf20Sopenharmony_ci
16698c2ecf20Sopenharmony_ci	/*
16708c2ecf20Sopenharmony_ci	 * Fill in command slot information.
16718c2ecf20Sopenharmony_ci	 */
16728c2ecf20Sopenharmony_ci	opts = cmd_fis_len | n_elem << 16 | (qc->dev->link->pmp << 12);
16738c2ecf20Sopenharmony_ci	if (qc->tf.flags & ATA_TFLAG_WRITE)
16748c2ecf20Sopenharmony_ci		opts |= AHCI_CMD_WRITE;
16758c2ecf20Sopenharmony_ci	if (is_atapi)
16768c2ecf20Sopenharmony_ci		opts |= AHCI_CMD_ATAPI | AHCI_CMD_PREFETCH;
16778c2ecf20Sopenharmony_ci
16788c2ecf20Sopenharmony_ci	ahci_fill_cmd_slot(pp, qc->hw_tag, opts);
16798c2ecf20Sopenharmony_ci
16808c2ecf20Sopenharmony_ci	return AC_ERR_OK;
16818c2ecf20Sopenharmony_ci}
16828c2ecf20Sopenharmony_ci
16838c2ecf20Sopenharmony_cistatic void ahci_fbs_dec_intr(struct ata_port *ap)
16848c2ecf20Sopenharmony_ci{
16858c2ecf20Sopenharmony_ci	struct ahci_port_priv *pp = ap->private_data;
16868c2ecf20Sopenharmony_ci	void __iomem *port_mmio = ahci_port_base(ap);
16878c2ecf20Sopenharmony_ci	u32 fbs = readl(port_mmio + PORT_FBS);
16888c2ecf20Sopenharmony_ci	int retries = 3;
16898c2ecf20Sopenharmony_ci
16908c2ecf20Sopenharmony_ci	DPRINTK("ENTER\n");
16918c2ecf20Sopenharmony_ci	BUG_ON(!pp->fbs_enabled);
16928c2ecf20Sopenharmony_ci
16938c2ecf20Sopenharmony_ci	/* time to wait for DEC is not specified by AHCI spec,
16948c2ecf20Sopenharmony_ci	 * add a retry loop for safety.
16958c2ecf20Sopenharmony_ci	 */
16968c2ecf20Sopenharmony_ci	writel(fbs | PORT_FBS_DEC, port_mmio + PORT_FBS);
16978c2ecf20Sopenharmony_ci	fbs = readl(port_mmio + PORT_FBS);
16988c2ecf20Sopenharmony_ci	while ((fbs & PORT_FBS_DEC) && retries--) {
16998c2ecf20Sopenharmony_ci		udelay(1);
17008c2ecf20Sopenharmony_ci		fbs = readl(port_mmio + PORT_FBS);
17018c2ecf20Sopenharmony_ci	}
17028c2ecf20Sopenharmony_ci
17038c2ecf20Sopenharmony_ci	if (fbs & PORT_FBS_DEC)
17048c2ecf20Sopenharmony_ci		dev_err(ap->host->dev, "failed to clear device error\n");
17058c2ecf20Sopenharmony_ci}
17068c2ecf20Sopenharmony_ci
17078c2ecf20Sopenharmony_cistatic void ahci_error_intr(struct ata_port *ap, u32 irq_stat)
17088c2ecf20Sopenharmony_ci{
17098c2ecf20Sopenharmony_ci	struct ahci_host_priv *hpriv = ap->host->private_data;
17108c2ecf20Sopenharmony_ci	struct ahci_port_priv *pp = ap->private_data;
17118c2ecf20Sopenharmony_ci	struct ata_eh_info *host_ehi = &ap->link.eh_info;
17128c2ecf20Sopenharmony_ci	struct ata_link *link = NULL;
17138c2ecf20Sopenharmony_ci	struct ata_queued_cmd *active_qc;
17148c2ecf20Sopenharmony_ci	struct ata_eh_info *active_ehi;
17158c2ecf20Sopenharmony_ci	bool fbs_need_dec = false;
17168c2ecf20Sopenharmony_ci	u32 serror;
17178c2ecf20Sopenharmony_ci
17188c2ecf20Sopenharmony_ci	/* determine active link with error */
17198c2ecf20Sopenharmony_ci	if (pp->fbs_enabled) {
17208c2ecf20Sopenharmony_ci		void __iomem *port_mmio = ahci_port_base(ap);
17218c2ecf20Sopenharmony_ci		u32 fbs = readl(port_mmio + PORT_FBS);
17228c2ecf20Sopenharmony_ci		int pmp = fbs >> PORT_FBS_DWE_OFFSET;
17238c2ecf20Sopenharmony_ci
17248c2ecf20Sopenharmony_ci		if ((fbs & PORT_FBS_SDE) && (pmp < ap->nr_pmp_links)) {
17258c2ecf20Sopenharmony_ci			link = &ap->pmp_link[pmp];
17268c2ecf20Sopenharmony_ci			fbs_need_dec = true;
17278c2ecf20Sopenharmony_ci		}
17288c2ecf20Sopenharmony_ci
17298c2ecf20Sopenharmony_ci	} else
17308c2ecf20Sopenharmony_ci		ata_for_each_link(link, ap, EDGE)
17318c2ecf20Sopenharmony_ci			if (ata_link_active(link))
17328c2ecf20Sopenharmony_ci				break;
17338c2ecf20Sopenharmony_ci
17348c2ecf20Sopenharmony_ci	if (!link)
17358c2ecf20Sopenharmony_ci		link = &ap->link;
17368c2ecf20Sopenharmony_ci
17378c2ecf20Sopenharmony_ci	active_qc = ata_qc_from_tag(ap, link->active_tag);
17388c2ecf20Sopenharmony_ci	active_ehi = &link->eh_info;
17398c2ecf20Sopenharmony_ci
17408c2ecf20Sopenharmony_ci	/* record irq stat */
17418c2ecf20Sopenharmony_ci	ata_ehi_clear_desc(host_ehi);
17428c2ecf20Sopenharmony_ci	ata_ehi_push_desc(host_ehi, "irq_stat 0x%08x", irq_stat);
17438c2ecf20Sopenharmony_ci
17448c2ecf20Sopenharmony_ci	/* AHCI needs SError cleared; otherwise, it might lock up */
17458c2ecf20Sopenharmony_ci	ahci_scr_read(&ap->link, SCR_ERROR, &serror);
17468c2ecf20Sopenharmony_ci	ahci_scr_write(&ap->link, SCR_ERROR, serror);
17478c2ecf20Sopenharmony_ci	host_ehi->serror |= serror;
17488c2ecf20Sopenharmony_ci
17498c2ecf20Sopenharmony_ci	/* some controllers set IRQ_IF_ERR on device errors, ignore it */
17508c2ecf20Sopenharmony_ci	if (hpriv->flags & AHCI_HFLAG_IGN_IRQ_IF_ERR)
17518c2ecf20Sopenharmony_ci		irq_stat &= ~PORT_IRQ_IF_ERR;
17528c2ecf20Sopenharmony_ci
17538c2ecf20Sopenharmony_ci	if (irq_stat & PORT_IRQ_TF_ERR) {
17548c2ecf20Sopenharmony_ci		/* If qc is active, charge it; otherwise, the active
17558c2ecf20Sopenharmony_ci		 * link.  There's no active qc on NCQ errors.  It will
17568c2ecf20Sopenharmony_ci		 * be determined by EH by reading log page 10h.
17578c2ecf20Sopenharmony_ci		 */
17588c2ecf20Sopenharmony_ci		if (active_qc)
17598c2ecf20Sopenharmony_ci			active_qc->err_mask |= AC_ERR_DEV;
17608c2ecf20Sopenharmony_ci		else
17618c2ecf20Sopenharmony_ci			active_ehi->err_mask |= AC_ERR_DEV;
17628c2ecf20Sopenharmony_ci
17638c2ecf20Sopenharmony_ci		if (hpriv->flags & AHCI_HFLAG_IGN_SERR_INTERNAL)
17648c2ecf20Sopenharmony_ci			host_ehi->serror &= ~SERR_INTERNAL;
17658c2ecf20Sopenharmony_ci	}
17668c2ecf20Sopenharmony_ci
17678c2ecf20Sopenharmony_ci	if (irq_stat & PORT_IRQ_UNK_FIS) {
17688c2ecf20Sopenharmony_ci		u32 *unk = pp->rx_fis + RX_FIS_UNK;
17698c2ecf20Sopenharmony_ci
17708c2ecf20Sopenharmony_ci		active_ehi->err_mask |= AC_ERR_HSM;
17718c2ecf20Sopenharmony_ci		active_ehi->action |= ATA_EH_RESET;
17728c2ecf20Sopenharmony_ci		ata_ehi_push_desc(active_ehi,
17738c2ecf20Sopenharmony_ci				  "unknown FIS %08x %08x %08x %08x" ,
17748c2ecf20Sopenharmony_ci				  unk[0], unk[1], unk[2], unk[3]);
17758c2ecf20Sopenharmony_ci	}
17768c2ecf20Sopenharmony_ci
17778c2ecf20Sopenharmony_ci	if (sata_pmp_attached(ap) && (irq_stat & PORT_IRQ_BAD_PMP)) {
17788c2ecf20Sopenharmony_ci		active_ehi->err_mask |= AC_ERR_HSM;
17798c2ecf20Sopenharmony_ci		active_ehi->action |= ATA_EH_RESET;
17808c2ecf20Sopenharmony_ci		ata_ehi_push_desc(active_ehi, "incorrect PMP");
17818c2ecf20Sopenharmony_ci	}
17828c2ecf20Sopenharmony_ci
17838c2ecf20Sopenharmony_ci	if (irq_stat & (PORT_IRQ_HBUS_ERR | PORT_IRQ_HBUS_DATA_ERR)) {
17848c2ecf20Sopenharmony_ci		host_ehi->err_mask |= AC_ERR_HOST_BUS;
17858c2ecf20Sopenharmony_ci		host_ehi->action |= ATA_EH_RESET;
17868c2ecf20Sopenharmony_ci		ata_ehi_push_desc(host_ehi, "host bus error");
17878c2ecf20Sopenharmony_ci	}
17888c2ecf20Sopenharmony_ci
17898c2ecf20Sopenharmony_ci	if (irq_stat & PORT_IRQ_IF_ERR) {
17908c2ecf20Sopenharmony_ci		if (fbs_need_dec)
17918c2ecf20Sopenharmony_ci			active_ehi->err_mask |= AC_ERR_DEV;
17928c2ecf20Sopenharmony_ci		else {
17938c2ecf20Sopenharmony_ci			host_ehi->err_mask |= AC_ERR_ATA_BUS;
17948c2ecf20Sopenharmony_ci			host_ehi->action |= ATA_EH_RESET;
17958c2ecf20Sopenharmony_ci		}
17968c2ecf20Sopenharmony_ci
17978c2ecf20Sopenharmony_ci		ata_ehi_push_desc(host_ehi, "interface fatal error");
17988c2ecf20Sopenharmony_ci	}
17998c2ecf20Sopenharmony_ci
18008c2ecf20Sopenharmony_ci	if (irq_stat & (PORT_IRQ_CONNECT | PORT_IRQ_PHYRDY)) {
18018c2ecf20Sopenharmony_ci		ata_ehi_hotplugged(host_ehi);
18028c2ecf20Sopenharmony_ci		ata_ehi_push_desc(host_ehi, "%s",
18038c2ecf20Sopenharmony_ci			irq_stat & PORT_IRQ_CONNECT ?
18048c2ecf20Sopenharmony_ci			"connection status changed" : "PHY RDY changed");
18058c2ecf20Sopenharmony_ci	}
18068c2ecf20Sopenharmony_ci
18078c2ecf20Sopenharmony_ci	/* okay, let's hand over to EH */
18088c2ecf20Sopenharmony_ci
18098c2ecf20Sopenharmony_ci	if (irq_stat & PORT_IRQ_FREEZE)
18108c2ecf20Sopenharmony_ci		ata_port_freeze(ap);
18118c2ecf20Sopenharmony_ci	else if (fbs_need_dec) {
18128c2ecf20Sopenharmony_ci		ata_link_abort(link);
18138c2ecf20Sopenharmony_ci		ahci_fbs_dec_intr(ap);
18148c2ecf20Sopenharmony_ci	} else
18158c2ecf20Sopenharmony_ci		ata_port_abort(ap);
18168c2ecf20Sopenharmony_ci}
18178c2ecf20Sopenharmony_ci
18188c2ecf20Sopenharmony_cistatic void ahci_handle_port_interrupt(struct ata_port *ap,
18198c2ecf20Sopenharmony_ci				       void __iomem *port_mmio, u32 status)
18208c2ecf20Sopenharmony_ci{
18218c2ecf20Sopenharmony_ci	struct ata_eh_info *ehi = &ap->link.eh_info;
18228c2ecf20Sopenharmony_ci	struct ahci_port_priv *pp = ap->private_data;
18238c2ecf20Sopenharmony_ci	struct ahci_host_priv *hpriv = ap->host->private_data;
18248c2ecf20Sopenharmony_ci	int resetting = !!(ap->pflags & ATA_PFLAG_RESETTING);
18258c2ecf20Sopenharmony_ci	u32 qc_active = 0;
18268c2ecf20Sopenharmony_ci	int rc;
18278c2ecf20Sopenharmony_ci
18288c2ecf20Sopenharmony_ci	/* ignore BAD_PMP while resetting */
18298c2ecf20Sopenharmony_ci	if (unlikely(resetting))
18308c2ecf20Sopenharmony_ci		status &= ~PORT_IRQ_BAD_PMP;
18318c2ecf20Sopenharmony_ci
18328c2ecf20Sopenharmony_ci	if (sata_lpm_ignore_phy_events(&ap->link)) {
18338c2ecf20Sopenharmony_ci		status &= ~PORT_IRQ_PHYRDY;
18348c2ecf20Sopenharmony_ci		ahci_scr_write(&ap->link, SCR_ERROR, SERR_PHYRDY_CHG);
18358c2ecf20Sopenharmony_ci	}
18368c2ecf20Sopenharmony_ci
18378c2ecf20Sopenharmony_ci	if (unlikely(status & PORT_IRQ_ERROR)) {
18388c2ecf20Sopenharmony_ci		ahci_error_intr(ap, status);
18398c2ecf20Sopenharmony_ci		return;
18408c2ecf20Sopenharmony_ci	}
18418c2ecf20Sopenharmony_ci
18428c2ecf20Sopenharmony_ci	if (status & PORT_IRQ_SDB_FIS) {
18438c2ecf20Sopenharmony_ci		/* If SNotification is available, leave notification
18448c2ecf20Sopenharmony_ci		 * handling to sata_async_notification().  If not,
18458c2ecf20Sopenharmony_ci		 * emulate it by snooping SDB FIS RX area.
18468c2ecf20Sopenharmony_ci		 *
18478c2ecf20Sopenharmony_ci		 * Snooping FIS RX area is probably cheaper than
18488c2ecf20Sopenharmony_ci		 * poking SNotification but some constrollers which
18498c2ecf20Sopenharmony_ci		 * implement SNotification, ICH9 for example, don't
18508c2ecf20Sopenharmony_ci		 * store AN SDB FIS into receive area.
18518c2ecf20Sopenharmony_ci		 */
18528c2ecf20Sopenharmony_ci		if (hpriv->cap & HOST_CAP_SNTF)
18538c2ecf20Sopenharmony_ci			sata_async_notification(ap);
18548c2ecf20Sopenharmony_ci		else {
18558c2ecf20Sopenharmony_ci			/* If the 'N' bit in word 0 of the FIS is set,
18568c2ecf20Sopenharmony_ci			 * we just received asynchronous notification.
18578c2ecf20Sopenharmony_ci			 * Tell libata about it.
18588c2ecf20Sopenharmony_ci			 *
18598c2ecf20Sopenharmony_ci			 * Lack of SNotification should not appear in
18608c2ecf20Sopenharmony_ci			 * ahci 1.2, so the workaround is unnecessary
18618c2ecf20Sopenharmony_ci			 * when FBS is enabled.
18628c2ecf20Sopenharmony_ci			 */
18638c2ecf20Sopenharmony_ci			if (pp->fbs_enabled)
18648c2ecf20Sopenharmony_ci				WARN_ON_ONCE(1);
18658c2ecf20Sopenharmony_ci			else {
18668c2ecf20Sopenharmony_ci				const __le32 *f = pp->rx_fis + RX_FIS_SDB;
18678c2ecf20Sopenharmony_ci				u32 f0 = le32_to_cpu(f[0]);
18688c2ecf20Sopenharmony_ci				if (f0 & (1 << 15))
18698c2ecf20Sopenharmony_ci					sata_async_notification(ap);
18708c2ecf20Sopenharmony_ci			}
18718c2ecf20Sopenharmony_ci		}
18728c2ecf20Sopenharmony_ci	}
18738c2ecf20Sopenharmony_ci
18748c2ecf20Sopenharmony_ci	/* pp->active_link is not reliable once FBS is enabled, both
18758c2ecf20Sopenharmony_ci	 * PORT_SCR_ACT and PORT_CMD_ISSUE should be checked because
18768c2ecf20Sopenharmony_ci	 * NCQ and non-NCQ commands may be in flight at the same time.
18778c2ecf20Sopenharmony_ci	 */
18788c2ecf20Sopenharmony_ci	if (pp->fbs_enabled) {
18798c2ecf20Sopenharmony_ci		if (ap->qc_active) {
18808c2ecf20Sopenharmony_ci			qc_active = readl(port_mmio + PORT_SCR_ACT);
18818c2ecf20Sopenharmony_ci			qc_active |= readl(port_mmio + PORT_CMD_ISSUE);
18828c2ecf20Sopenharmony_ci		}
18838c2ecf20Sopenharmony_ci	} else {
18848c2ecf20Sopenharmony_ci		/* pp->active_link is valid iff any command is in flight */
18858c2ecf20Sopenharmony_ci		if (ap->qc_active && pp->active_link->sactive)
18868c2ecf20Sopenharmony_ci			qc_active = readl(port_mmio + PORT_SCR_ACT);
18878c2ecf20Sopenharmony_ci		else
18888c2ecf20Sopenharmony_ci			qc_active = readl(port_mmio + PORT_CMD_ISSUE);
18898c2ecf20Sopenharmony_ci	}
18908c2ecf20Sopenharmony_ci
18918c2ecf20Sopenharmony_ci
18928c2ecf20Sopenharmony_ci	rc = ata_qc_complete_multiple(ap, qc_active);
18938c2ecf20Sopenharmony_ci
18948c2ecf20Sopenharmony_ci	/* while resetting, invalid completions are expected */
18958c2ecf20Sopenharmony_ci	if (unlikely(rc < 0 && !resetting)) {
18968c2ecf20Sopenharmony_ci		ehi->err_mask |= AC_ERR_HSM;
18978c2ecf20Sopenharmony_ci		ehi->action |= ATA_EH_RESET;
18988c2ecf20Sopenharmony_ci		ata_port_freeze(ap);
18998c2ecf20Sopenharmony_ci	}
19008c2ecf20Sopenharmony_ci}
19018c2ecf20Sopenharmony_ci
19028c2ecf20Sopenharmony_cistatic void ahci_port_intr(struct ata_port *ap)
19038c2ecf20Sopenharmony_ci{
19048c2ecf20Sopenharmony_ci	void __iomem *port_mmio = ahci_port_base(ap);
19058c2ecf20Sopenharmony_ci	u32 status;
19068c2ecf20Sopenharmony_ci
19078c2ecf20Sopenharmony_ci	status = readl(port_mmio + PORT_IRQ_STAT);
19088c2ecf20Sopenharmony_ci	writel(status, port_mmio + PORT_IRQ_STAT);
19098c2ecf20Sopenharmony_ci
19108c2ecf20Sopenharmony_ci	ahci_handle_port_interrupt(ap, port_mmio, status);
19118c2ecf20Sopenharmony_ci}
19128c2ecf20Sopenharmony_ci
19138c2ecf20Sopenharmony_cistatic irqreturn_t ahci_multi_irqs_intr_hard(int irq, void *dev_instance)
19148c2ecf20Sopenharmony_ci{
19158c2ecf20Sopenharmony_ci	struct ata_port *ap = dev_instance;
19168c2ecf20Sopenharmony_ci	void __iomem *port_mmio = ahci_port_base(ap);
19178c2ecf20Sopenharmony_ci	u32 status;
19188c2ecf20Sopenharmony_ci
19198c2ecf20Sopenharmony_ci	status = readl(port_mmio + PORT_IRQ_STAT);
19208c2ecf20Sopenharmony_ci	writel(status, port_mmio + PORT_IRQ_STAT);
19218c2ecf20Sopenharmony_ci
19228c2ecf20Sopenharmony_ci	spin_lock(ap->lock);
19238c2ecf20Sopenharmony_ci	ahci_handle_port_interrupt(ap, port_mmio, status);
19248c2ecf20Sopenharmony_ci	spin_unlock(ap->lock);
19258c2ecf20Sopenharmony_ci
19268c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
19278c2ecf20Sopenharmony_ci}
19288c2ecf20Sopenharmony_ci
19298c2ecf20Sopenharmony_ciu32 ahci_handle_port_intr(struct ata_host *host, u32 irq_masked)
19308c2ecf20Sopenharmony_ci{
19318c2ecf20Sopenharmony_ci	unsigned int i, handled = 0;
19328c2ecf20Sopenharmony_ci
19338c2ecf20Sopenharmony_ci	for (i = 0; i < host->n_ports; i++) {
19348c2ecf20Sopenharmony_ci		struct ata_port *ap;
19358c2ecf20Sopenharmony_ci
19368c2ecf20Sopenharmony_ci		if (!(irq_masked & (1 << i)))
19378c2ecf20Sopenharmony_ci			continue;
19388c2ecf20Sopenharmony_ci
19398c2ecf20Sopenharmony_ci		ap = host->ports[i];
19408c2ecf20Sopenharmony_ci		if (ap) {
19418c2ecf20Sopenharmony_ci			ahci_port_intr(ap);
19428c2ecf20Sopenharmony_ci		} else {
19438c2ecf20Sopenharmony_ci			if (ata_ratelimit())
19448c2ecf20Sopenharmony_ci				dev_warn(host->dev,
19458c2ecf20Sopenharmony_ci					 "interrupt on disabled port %u\n", i);
19468c2ecf20Sopenharmony_ci		}
19478c2ecf20Sopenharmony_ci
19488c2ecf20Sopenharmony_ci		handled = 1;
19498c2ecf20Sopenharmony_ci	}
19508c2ecf20Sopenharmony_ci
19518c2ecf20Sopenharmony_ci	return handled;
19528c2ecf20Sopenharmony_ci}
19538c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ahci_handle_port_intr);
19548c2ecf20Sopenharmony_ci
19558c2ecf20Sopenharmony_cistatic irqreturn_t ahci_single_level_irq_intr(int irq, void *dev_instance)
19568c2ecf20Sopenharmony_ci{
19578c2ecf20Sopenharmony_ci	struct ata_host *host = dev_instance;
19588c2ecf20Sopenharmony_ci	struct ahci_host_priv *hpriv;
19598c2ecf20Sopenharmony_ci	unsigned int rc = 0;
19608c2ecf20Sopenharmony_ci	void __iomem *mmio;
19618c2ecf20Sopenharmony_ci	u32 irq_stat, irq_masked;
19628c2ecf20Sopenharmony_ci
19638c2ecf20Sopenharmony_ci	hpriv = host->private_data;
19648c2ecf20Sopenharmony_ci	mmio = hpriv->mmio;
19658c2ecf20Sopenharmony_ci
19668c2ecf20Sopenharmony_ci	/* sigh.  0xffffffff is a valid return from h/w */
19678c2ecf20Sopenharmony_ci	irq_stat = readl(mmio + HOST_IRQ_STAT);
19688c2ecf20Sopenharmony_ci	if (!irq_stat)
19698c2ecf20Sopenharmony_ci		return IRQ_NONE;
19708c2ecf20Sopenharmony_ci
19718c2ecf20Sopenharmony_ci	irq_masked = irq_stat & hpriv->port_map;
19728c2ecf20Sopenharmony_ci
19738c2ecf20Sopenharmony_ci	spin_lock(&host->lock);
19748c2ecf20Sopenharmony_ci
19758c2ecf20Sopenharmony_ci	rc = ahci_handle_port_intr(host, irq_masked);
19768c2ecf20Sopenharmony_ci
19778c2ecf20Sopenharmony_ci	/* HOST_IRQ_STAT behaves as level triggered latch meaning that
19788c2ecf20Sopenharmony_ci	 * it should be cleared after all the port events are cleared;
19798c2ecf20Sopenharmony_ci	 * otherwise, it will raise a spurious interrupt after each
19808c2ecf20Sopenharmony_ci	 * valid one.  Please read section 10.6.2 of ahci 1.1 for more
19818c2ecf20Sopenharmony_ci	 * information.
19828c2ecf20Sopenharmony_ci	 *
19838c2ecf20Sopenharmony_ci	 * Also, use the unmasked value to clear interrupt as spurious
19848c2ecf20Sopenharmony_ci	 * pending event on a dummy port might cause screaming IRQ.
19858c2ecf20Sopenharmony_ci	 */
19868c2ecf20Sopenharmony_ci	writel(irq_stat, mmio + HOST_IRQ_STAT);
19878c2ecf20Sopenharmony_ci
19888c2ecf20Sopenharmony_ci	spin_unlock(&host->lock);
19898c2ecf20Sopenharmony_ci
19908c2ecf20Sopenharmony_ci	return IRQ_RETVAL(rc);
19918c2ecf20Sopenharmony_ci}
19928c2ecf20Sopenharmony_ci
19938c2ecf20Sopenharmony_ciunsigned int ahci_qc_issue(struct ata_queued_cmd *qc)
19948c2ecf20Sopenharmony_ci{
19958c2ecf20Sopenharmony_ci	struct ata_port *ap = qc->ap;
19968c2ecf20Sopenharmony_ci	void __iomem *port_mmio = ahci_port_base(ap);
19978c2ecf20Sopenharmony_ci	struct ahci_port_priv *pp = ap->private_data;
19988c2ecf20Sopenharmony_ci
19998c2ecf20Sopenharmony_ci	/* Keep track of the currently active link.  It will be used
20008c2ecf20Sopenharmony_ci	 * in completion path to determine whether NCQ phase is in
20018c2ecf20Sopenharmony_ci	 * progress.
20028c2ecf20Sopenharmony_ci	 */
20038c2ecf20Sopenharmony_ci	pp->active_link = qc->dev->link;
20048c2ecf20Sopenharmony_ci
20058c2ecf20Sopenharmony_ci	if (ata_is_ncq(qc->tf.protocol))
20068c2ecf20Sopenharmony_ci		writel(1 << qc->hw_tag, port_mmio + PORT_SCR_ACT);
20078c2ecf20Sopenharmony_ci
20088c2ecf20Sopenharmony_ci	if (pp->fbs_enabled && pp->fbs_last_dev != qc->dev->link->pmp) {
20098c2ecf20Sopenharmony_ci		u32 fbs = readl(port_mmio + PORT_FBS);
20108c2ecf20Sopenharmony_ci		fbs &= ~(PORT_FBS_DEV_MASK | PORT_FBS_DEC);
20118c2ecf20Sopenharmony_ci		fbs |= qc->dev->link->pmp << PORT_FBS_DEV_OFFSET;
20128c2ecf20Sopenharmony_ci		writel(fbs, port_mmio + PORT_FBS);
20138c2ecf20Sopenharmony_ci		pp->fbs_last_dev = qc->dev->link->pmp;
20148c2ecf20Sopenharmony_ci	}
20158c2ecf20Sopenharmony_ci
20168c2ecf20Sopenharmony_ci	writel(1 << qc->hw_tag, port_mmio + PORT_CMD_ISSUE);
20178c2ecf20Sopenharmony_ci
20188c2ecf20Sopenharmony_ci	ahci_sw_activity(qc->dev->link);
20198c2ecf20Sopenharmony_ci
20208c2ecf20Sopenharmony_ci	return 0;
20218c2ecf20Sopenharmony_ci}
20228c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ahci_qc_issue);
20238c2ecf20Sopenharmony_ci
20248c2ecf20Sopenharmony_cistatic bool ahci_qc_fill_rtf(struct ata_queued_cmd *qc)
20258c2ecf20Sopenharmony_ci{
20268c2ecf20Sopenharmony_ci	struct ahci_port_priv *pp = qc->ap->private_data;
20278c2ecf20Sopenharmony_ci	u8 *rx_fis = pp->rx_fis;
20288c2ecf20Sopenharmony_ci
20298c2ecf20Sopenharmony_ci	if (pp->fbs_enabled)
20308c2ecf20Sopenharmony_ci		rx_fis += qc->dev->link->pmp * AHCI_RX_FIS_SZ;
20318c2ecf20Sopenharmony_ci
20328c2ecf20Sopenharmony_ci	/*
20338c2ecf20Sopenharmony_ci	 * After a successful execution of an ATA PIO data-in command,
20348c2ecf20Sopenharmony_ci	 * the device doesn't send D2H Reg FIS to update the TF and
20358c2ecf20Sopenharmony_ci	 * the host should take TF and E_Status from the preceding PIO
20368c2ecf20Sopenharmony_ci	 * Setup FIS.
20378c2ecf20Sopenharmony_ci	 */
20388c2ecf20Sopenharmony_ci	if (qc->tf.protocol == ATA_PROT_PIO && qc->dma_dir == DMA_FROM_DEVICE &&
20398c2ecf20Sopenharmony_ci	    !(qc->flags & ATA_QCFLAG_FAILED)) {
20408c2ecf20Sopenharmony_ci		ata_tf_from_fis(rx_fis + RX_FIS_PIO_SETUP, &qc->result_tf);
20418c2ecf20Sopenharmony_ci		qc->result_tf.command = (rx_fis + RX_FIS_PIO_SETUP)[15];
20428c2ecf20Sopenharmony_ci	} else
20438c2ecf20Sopenharmony_ci		ata_tf_from_fis(rx_fis + RX_FIS_D2H_REG, &qc->result_tf);
20448c2ecf20Sopenharmony_ci
20458c2ecf20Sopenharmony_ci	return true;
20468c2ecf20Sopenharmony_ci}
20478c2ecf20Sopenharmony_ci
20488c2ecf20Sopenharmony_cistatic void ahci_freeze(struct ata_port *ap)
20498c2ecf20Sopenharmony_ci{
20508c2ecf20Sopenharmony_ci	void __iomem *port_mmio = ahci_port_base(ap);
20518c2ecf20Sopenharmony_ci
20528c2ecf20Sopenharmony_ci	/* turn IRQ off */
20538c2ecf20Sopenharmony_ci	writel(0, port_mmio + PORT_IRQ_MASK);
20548c2ecf20Sopenharmony_ci}
20558c2ecf20Sopenharmony_ci
20568c2ecf20Sopenharmony_cistatic void ahci_thaw(struct ata_port *ap)
20578c2ecf20Sopenharmony_ci{
20588c2ecf20Sopenharmony_ci	struct ahci_host_priv *hpriv = ap->host->private_data;
20598c2ecf20Sopenharmony_ci	void __iomem *mmio = hpriv->mmio;
20608c2ecf20Sopenharmony_ci	void __iomem *port_mmio = ahci_port_base(ap);
20618c2ecf20Sopenharmony_ci	u32 tmp;
20628c2ecf20Sopenharmony_ci	struct ahci_port_priv *pp = ap->private_data;
20638c2ecf20Sopenharmony_ci
20648c2ecf20Sopenharmony_ci	/* clear IRQ */
20658c2ecf20Sopenharmony_ci	tmp = readl(port_mmio + PORT_IRQ_STAT);
20668c2ecf20Sopenharmony_ci	writel(tmp, port_mmio + PORT_IRQ_STAT);
20678c2ecf20Sopenharmony_ci	writel(1 << ap->port_no, mmio + HOST_IRQ_STAT);
20688c2ecf20Sopenharmony_ci
20698c2ecf20Sopenharmony_ci	/* turn IRQ back on */
20708c2ecf20Sopenharmony_ci	writel(pp->intr_mask, port_mmio + PORT_IRQ_MASK);
20718c2ecf20Sopenharmony_ci}
20728c2ecf20Sopenharmony_ci
20738c2ecf20Sopenharmony_civoid ahci_error_handler(struct ata_port *ap)
20748c2ecf20Sopenharmony_ci{
20758c2ecf20Sopenharmony_ci	struct ahci_host_priv *hpriv = ap->host->private_data;
20768c2ecf20Sopenharmony_ci
20778c2ecf20Sopenharmony_ci	if (!(ap->pflags & ATA_PFLAG_FROZEN)) {
20788c2ecf20Sopenharmony_ci		/* restart engine */
20798c2ecf20Sopenharmony_ci		hpriv->stop_engine(ap);
20808c2ecf20Sopenharmony_ci		hpriv->start_engine(ap);
20818c2ecf20Sopenharmony_ci	}
20828c2ecf20Sopenharmony_ci
20838c2ecf20Sopenharmony_ci	sata_pmp_error_handler(ap);
20848c2ecf20Sopenharmony_ci
20858c2ecf20Sopenharmony_ci	if (!ata_dev_enabled(ap->link.device))
20868c2ecf20Sopenharmony_ci		hpriv->stop_engine(ap);
20878c2ecf20Sopenharmony_ci}
20888c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ahci_error_handler);
20898c2ecf20Sopenharmony_ci
20908c2ecf20Sopenharmony_cistatic void ahci_post_internal_cmd(struct ata_queued_cmd *qc)
20918c2ecf20Sopenharmony_ci{
20928c2ecf20Sopenharmony_ci	struct ata_port *ap = qc->ap;
20938c2ecf20Sopenharmony_ci
20948c2ecf20Sopenharmony_ci	/* make DMA engine forget about the failed command */
20958c2ecf20Sopenharmony_ci	if (qc->flags & ATA_QCFLAG_FAILED)
20968c2ecf20Sopenharmony_ci		ahci_kick_engine(ap);
20978c2ecf20Sopenharmony_ci}
20988c2ecf20Sopenharmony_ci
20998c2ecf20Sopenharmony_cistatic void ahci_set_aggressive_devslp(struct ata_port *ap, bool sleep)
21008c2ecf20Sopenharmony_ci{
21018c2ecf20Sopenharmony_ci	struct ahci_host_priv *hpriv = ap->host->private_data;
21028c2ecf20Sopenharmony_ci	void __iomem *port_mmio = ahci_port_base(ap);
21038c2ecf20Sopenharmony_ci	struct ata_device *dev = ap->link.device;
21048c2ecf20Sopenharmony_ci	u32 devslp, dm, dito, mdat, deto, dito_conf;
21058c2ecf20Sopenharmony_ci	int rc;
21068c2ecf20Sopenharmony_ci	unsigned int err_mask;
21078c2ecf20Sopenharmony_ci
21088c2ecf20Sopenharmony_ci	devslp = readl(port_mmio + PORT_DEVSLP);
21098c2ecf20Sopenharmony_ci	if (!(devslp & PORT_DEVSLP_DSP)) {
21108c2ecf20Sopenharmony_ci		dev_info(ap->host->dev, "port does not support device sleep\n");
21118c2ecf20Sopenharmony_ci		return;
21128c2ecf20Sopenharmony_ci	}
21138c2ecf20Sopenharmony_ci
21148c2ecf20Sopenharmony_ci	/* disable device sleep */
21158c2ecf20Sopenharmony_ci	if (!sleep) {
21168c2ecf20Sopenharmony_ci		if (devslp & PORT_DEVSLP_ADSE) {
21178c2ecf20Sopenharmony_ci			writel(devslp & ~PORT_DEVSLP_ADSE,
21188c2ecf20Sopenharmony_ci			       port_mmio + PORT_DEVSLP);
21198c2ecf20Sopenharmony_ci			err_mask = ata_dev_set_feature(dev,
21208c2ecf20Sopenharmony_ci						       SETFEATURES_SATA_DISABLE,
21218c2ecf20Sopenharmony_ci						       SATA_DEVSLP);
21228c2ecf20Sopenharmony_ci			if (err_mask && err_mask != AC_ERR_DEV)
21238c2ecf20Sopenharmony_ci				ata_dev_warn(dev, "failed to disable DEVSLP\n");
21248c2ecf20Sopenharmony_ci		}
21258c2ecf20Sopenharmony_ci		return;
21268c2ecf20Sopenharmony_ci	}
21278c2ecf20Sopenharmony_ci
21288c2ecf20Sopenharmony_ci	dm = (devslp & PORT_DEVSLP_DM_MASK) >> PORT_DEVSLP_DM_OFFSET;
21298c2ecf20Sopenharmony_ci	dito = devslp_idle_timeout / (dm + 1);
21308c2ecf20Sopenharmony_ci	if (dito > 0x3ff)
21318c2ecf20Sopenharmony_ci		dito = 0x3ff;
21328c2ecf20Sopenharmony_ci
21338c2ecf20Sopenharmony_ci	dito_conf = (devslp >> PORT_DEVSLP_DITO_OFFSET) & 0x3FF;
21348c2ecf20Sopenharmony_ci
21358c2ecf20Sopenharmony_ci	/* device sleep was already enabled and same dito */
21368c2ecf20Sopenharmony_ci	if ((devslp & PORT_DEVSLP_ADSE) && (dito_conf == dito))
21378c2ecf20Sopenharmony_ci		return;
21388c2ecf20Sopenharmony_ci
21398c2ecf20Sopenharmony_ci	/* set DITO, MDAT, DETO and enable DevSlp, need to stop engine first */
21408c2ecf20Sopenharmony_ci	rc = hpriv->stop_engine(ap);
21418c2ecf20Sopenharmony_ci	if (rc)
21428c2ecf20Sopenharmony_ci		return;
21438c2ecf20Sopenharmony_ci
21448c2ecf20Sopenharmony_ci	/* Use the nominal value 10 ms if the read MDAT is zero,
21458c2ecf20Sopenharmony_ci	 * the nominal value of DETO is 20 ms.
21468c2ecf20Sopenharmony_ci	 */
21478c2ecf20Sopenharmony_ci	if (dev->devslp_timing[ATA_LOG_DEVSLP_VALID] &
21488c2ecf20Sopenharmony_ci	    ATA_LOG_DEVSLP_VALID_MASK) {
21498c2ecf20Sopenharmony_ci		mdat = dev->devslp_timing[ATA_LOG_DEVSLP_MDAT] &
21508c2ecf20Sopenharmony_ci		       ATA_LOG_DEVSLP_MDAT_MASK;
21518c2ecf20Sopenharmony_ci		if (!mdat)
21528c2ecf20Sopenharmony_ci			mdat = 10;
21538c2ecf20Sopenharmony_ci		deto = dev->devslp_timing[ATA_LOG_DEVSLP_DETO];
21548c2ecf20Sopenharmony_ci		if (!deto)
21558c2ecf20Sopenharmony_ci			deto = 20;
21568c2ecf20Sopenharmony_ci	} else {
21578c2ecf20Sopenharmony_ci		mdat = 10;
21588c2ecf20Sopenharmony_ci		deto = 20;
21598c2ecf20Sopenharmony_ci	}
21608c2ecf20Sopenharmony_ci
21618c2ecf20Sopenharmony_ci	/* Make dito, mdat, deto bits to 0s */
21628c2ecf20Sopenharmony_ci	devslp &= ~GENMASK_ULL(24, 2);
21638c2ecf20Sopenharmony_ci	devslp |= ((dito << PORT_DEVSLP_DITO_OFFSET) |
21648c2ecf20Sopenharmony_ci		   (mdat << PORT_DEVSLP_MDAT_OFFSET) |
21658c2ecf20Sopenharmony_ci		   (deto << PORT_DEVSLP_DETO_OFFSET) |
21668c2ecf20Sopenharmony_ci		   PORT_DEVSLP_ADSE);
21678c2ecf20Sopenharmony_ci	writel(devslp, port_mmio + PORT_DEVSLP);
21688c2ecf20Sopenharmony_ci
21698c2ecf20Sopenharmony_ci	hpriv->start_engine(ap);
21708c2ecf20Sopenharmony_ci
21718c2ecf20Sopenharmony_ci	/* enable device sleep feature for the drive */
21728c2ecf20Sopenharmony_ci	err_mask = ata_dev_set_feature(dev,
21738c2ecf20Sopenharmony_ci				       SETFEATURES_SATA_ENABLE,
21748c2ecf20Sopenharmony_ci				       SATA_DEVSLP);
21758c2ecf20Sopenharmony_ci	if (err_mask && err_mask != AC_ERR_DEV)
21768c2ecf20Sopenharmony_ci		ata_dev_warn(dev, "failed to enable DEVSLP\n");
21778c2ecf20Sopenharmony_ci}
21788c2ecf20Sopenharmony_ci
21798c2ecf20Sopenharmony_cistatic void ahci_enable_fbs(struct ata_port *ap)
21808c2ecf20Sopenharmony_ci{
21818c2ecf20Sopenharmony_ci	struct ahci_host_priv *hpriv = ap->host->private_data;
21828c2ecf20Sopenharmony_ci	struct ahci_port_priv *pp = ap->private_data;
21838c2ecf20Sopenharmony_ci	void __iomem *port_mmio = ahci_port_base(ap);
21848c2ecf20Sopenharmony_ci	u32 fbs;
21858c2ecf20Sopenharmony_ci	int rc;
21868c2ecf20Sopenharmony_ci
21878c2ecf20Sopenharmony_ci	if (!pp->fbs_supported)
21888c2ecf20Sopenharmony_ci		return;
21898c2ecf20Sopenharmony_ci
21908c2ecf20Sopenharmony_ci	fbs = readl(port_mmio + PORT_FBS);
21918c2ecf20Sopenharmony_ci	if (fbs & PORT_FBS_EN) {
21928c2ecf20Sopenharmony_ci		pp->fbs_enabled = true;
21938c2ecf20Sopenharmony_ci		pp->fbs_last_dev = -1; /* initialization */
21948c2ecf20Sopenharmony_ci		return;
21958c2ecf20Sopenharmony_ci	}
21968c2ecf20Sopenharmony_ci
21978c2ecf20Sopenharmony_ci	rc = hpriv->stop_engine(ap);
21988c2ecf20Sopenharmony_ci	if (rc)
21998c2ecf20Sopenharmony_ci		return;
22008c2ecf20Sopenharmony_ci
22018c2ecf20Sopenharmony_ci	writel(fbs | PORT_FBS_EN, port_mmio + PORT_FBS);
22028c2ecf20Sopenharmony_ci	fbs = readl(port_mmio + PORT_FBS);
22038c2ecf20Sopenharmony_ci	if (fbs & PORT_FBS_EN) {
22048c2ecf20Sopenharmony_ci		dev_info(ap->host->dev, "FBS is enabled\n");
22058c2ecf20Sopenharmony_ci		pp->fbs_enabled = true;
22068c2ecf20Sopenharmony_ci		pp->fbs_last_dev = -1; /* initialization */
22078c2ecf20Sopenharmony_ci	} else
22088c2ecf20Sopenharmony_ci		dev_err(ap->host->dev, "Failed to enable FBS\n");
22098c2ecf20Sopenharmony_ci
22108c2ecf20Sopenharmony_ci	hpriv->start_engine(ap);
22118c2ecf20Sopenharmony_ci}
22128c2ecf20Sopenharmony_ci
22138c2ecf20Sopenharmony_cistatic void ahci_disable_fbs(struct ata_port *ap)
22148c2ecf20Sopenharmony_ci{
22158c2ecf20Sopenharmony_ci	struct ahci_host_priv *hpriv = ap->host->private_data;
22168c2ecf20Sopenharmony_ci	struct ahci_port_priv *pp = ap->private_data;
22178c2ecf20Sopenharmony_ci	void __iomem *port_mmio = ahci_port_base(ap);
22188c2ecf20Sopenharmony_ci	u32 fbs;
22198c2ecf20Sopenharmony_ci	int rc;
22208c2ecf20Sopenharmony_ci
22218c2ecf20Sopenharmony_ci	if (!pp->fbs_supported)
22228c2ecf20Sopenharmony_ci		return;
22238c2ecf20Sopenharmony_ci
22248c2ecf20Sopenharmony_ci	fbs = readl(port_mmio + PORT_FBS);
22258c2ecf20Sopenharmony_ci	if ((fbs & PORT_FBS_EN) == 0) {
22268c2ecf20Sopenharmony_ci		pp->fbs_enabled = false;
22278c2ecf20Sopenharmony_ci		return;
22288c2ecf20Sopenharmony_ci	}
22298c2ecf20Sopenharmony_ci
22308c2ecf20Sopenharmony_ci	rc = hpriv->stop_engine(ap);
22318c2ecf20Sopenharmony_ci	if (rc)
22328c2ecf20Sopenharmony_ci		return;
22338c2ecf20Sopenharmony_ci
22348c2ecf20Sopenharmony_ci	writel(fbs & ~PORT_FBS_EN, port_mmio + PORT_FBS);
22358c2ecf20Sopenharmony_ci	fbs = readl(port_mmio + PORT_FBS);
22368c2ecf20Sopenharmony_ci	if (fbs & PORT_FBS_EN)
22378c2ecf20Sopenharmony_ci		dev_err(ap->host->dev, "Failed to disable FBS\n");
22388c2ecf20Sopenharmony_ci	else {
22398c2ecf20Sopenharmony_ci		dev_info(ap->host->dev, "FBS is disabled\n");
22408c2ecf20Sopenharmony_ci		pp->fbs_enabled = false;
22418c2ecf20Sopenharmony_ci	}
22428c2ecf20Sopenharmony_ci
22438c2ecf20Sopenharmony_ci	hpriv->start_engine(ap);
22448c2ecf20Sopenharmony_ci}
22458c2ecf20Sopenharmony_ci
22468c2ecf20Sopenharmony_cistatic void ahci_pmp_attach(struct ata_port *ap)
22478c2ecf20Sopenharmony_ci{
22488c2ecf20Sopenharmony_ci	void __iomem *port_mmio = ahci_port_base(ap);
22498c2ecf20Sopenharmony_ci	struct ahci_port_priv *pp = ap->private_data;
22508c2ecf20Sopenharmony_ci	u32 cmd;
22518c2ecf20Sopenharmony_ci
22528c2ecf20Sopenharmony_ci	cmd = readl(port_mmio + PORT_CMD);
22538c2ecf20Sopenharmony_ci	cmd |= PORT_CMD_PMP;
22548c2ecf20Sopenharmony_ci	writel(cmd, port_mmio + PORT_CMD);
22558c2ecf20Sopenharmony_ci
22568c2ecf20Sopenharmony_ci	ahci_enable_fbs(ap);
22578c2ecf20Sopenharmony_ci
22588c2ecf20Sopenharmony_ci	pp->intr_mask |= PORT_IRQ_BAD_PMP;
22598c2ecf20Sopenharmony_ci
22608c2ecf20Sopenharmony_ci	/*
22618c2ecf20Sopenharmony_ci	 * We must not change the port interrupt mask register if the
22628c2ecf20Sopenharmony_ci	 * port is marked frozen, the value in pp->intr_mask will be
22638c2ecf20Sopenharmony_ci	 * restored later when the port is thawed.
22648c2ecf20Sopenharmony_ci	 *
22658c2ecf20Sopenharmony_ci	 * Note that during initialization, the port is marked as
22668c2ecf20Sopenharmony_ci	 * frozen since the irq handler is not yet registered.
22678c2ecf20Sopenharmony_ci	 */
22688c2ecf20Sopenharmony_ci	if (!(ap->pflags & ATA_PFLAG_FROZEN))
22698c2ecf20Sopenharmony_ci		writel(pp->intr_mask, port_mmio + PORT_IRQ_MASK);
22708c2ecf20Sopenharmony_ci}
22718c2ecf20Sopenharmony_ci
22728c2ecf20Sopenharmony_cistatic void ahci_pmp_detach(struct ata_port *ap)
22738c2ecf20Sopenharmony_ci{
22748c2ecf20Sopenharmony_ci	void __iomem *port_mmio = ahci_port_base(ap);
22758c2ecf20Sopenharmony_ci	struct ahci_port_priv *pp = ap->private_data;
22768c2ecf20Sopenharmony_ci	u32 cmd;
22778c2ecf20Sopenharmony_ci
22788c2ecf20Sopenharmony_ci	ahci_disable_fbs(ap);
22798c2ecf20Sopenharmony_ci
22808c2ecf20Sopenharmony_ci	cmd = readl(port_mmio + PORT_CMD);
22818c2ecf20Sopenharmony_ci	cmd &= ~PORT_CMD_PMP;
22828c2ecf20Sopenharmony_ci	writel(cmd, port_mmio + PORT_CMD);
22838c2ecf20Sopenharmony_ci
22848c2ecf20Sopenharmony_ci	pp->intr_mask &= ~PORT_IRQ_BAD_PMP;
22858c2ecf20Sopenharmony_ci
22868c2ecf20Sopenharmony_ci	/* see comment above in ahci_pmp_attach() */
22878c2ecf20Sopenharmony_ci	if (!(ap->pflags & ATA_PFLAG_FROZEN))
22888c2ecf20Sopenharmony_ci		writel(pp->intr_mask, port_mmio + PORT_IRQ_MASK);
22898c2ecf20Sopenharmony_ci}
22908c2ecf20Sopenharmony_ci
22918c2ecf20Sopenharmony_ciint ahci_port_resume(struct ata_port *ap)
22928c2ecf20Sopenharmony_ci{
22938c2ecf20Sopenharmony_ci	ahci_rpm_get_port(ap);
22948c2ecf20Sopenharmony_ci
22958c2ecf20Sopenharmony_ci	ahci_power_up(ap);
22968c2ecf20Sopenharmony_ci	ahci_start_port(ap);
22978c2ecf20Sopenharmony_ci
22988c2ecf20Sopenharmony_ci	if (sata_pmp_attached(ap))
22998c2ecf20Sopenharmony_ci		ahci_pmp_attach(ap);
23008c2ecf20Sopenharmony_ci	else
23018c2ecf20Sopenharmony_ci		ahci_pmp_detach(ap);
23028c2ecf20Sopenharmony_ci
23038c2ecf20Sopenharmony_ci	return 0;
23048c2ecf20Sopenharmony_ci}
23058c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ahci_port_resume);
23068c2ecf20Sopenharmony_ci
23078c2ecf20Sopenharmony_ci#ifdef CONFIG_PM
23088c2ecf20Sopenharmony_cistatic int ahci_port_suspend(struct ata_port *ap, pm_message_t mesg)
23098c2ecf20Sopenharmony_ci{
23108c2ecf20Sopenharmony_ci	const char *emsg = NULL;
23118c2ecf20Sopenharmony_ci	int rc;
23128c2ecf20Sopenharmony_ci
23138c2ecf20Sopenharmony_ci	rc = ahci_deinit_port(ap, &emsg);
23148c2ecf20Sopenharmony_ci	if (rc == 0)
23158c2ecf20Sopenharmony_ci		ahci_power_down(ap);
23168c2ecf20Sopenharmony_ci	else {
23178c2ecf20Sopenharmony_ci		ata_port_err(ap, "%s (%d)\n", emsg, rc);
23188c2ecf20Sopenharmony_ci		ata_port_freeze(ap);
23198c2ecf20Sopenharmony_ci	}
23208c2ecf20Sopenharmony_ci
23218c2ecf20Sopenharmony_ci	ahci_rpm_put_port(ap);
23228c2ecf20Sopenharmony_ci	return rc;
23238c2ecf20Sopenharmony_ci}
23248c2ecf20Sopenharmony_ci#endif
23258c2ecf20Sopenharmony_ci
23268c2ecf20Sopenharmony_cistatic int ahci_port_start(struct ata_port *ap)
23278c2ecf20Sopenharmony_ci{
23288c2ecf20Sopenharmony_ci	struct ahci_host_priv *hpriv = ap->host->private_data;
23298c2ecf20Sopenharmony_ci	struct device *dev = ap->host->dev;
23308c2ecf20Sopenharmony_ci	struct ahci_port_priv *pp;
23318c2ecf20Sopenharmony_ci	void *mem;
23328c2ecf20Sopenharmony_ci	dma_addr_t mem_dma;
23338c2ecf20Sopenharmony_ci	size_t dma_sz, rx_fis_sz;
23348c2ecf20Sopenharmony_ci
23358c2ecf20Sopenharmony_ci	pp = devm_kzalloc(dev, sizeof(*pp), GFP_KERNEL);
23368c2ecf20Sopenharmony_ci	if (!pp)
23378c2ecf20Sopenharmony_ci		return -ENOMEM;
23388c2ecf20Sopenharmony_ci
23398c2ecf20Sopenharmony_ci	if (ap->host->n_ports > 1) {
23408c2ecf20Sopenharmony_ci		pp->irq_desc = devm_kzalloc(dev, 8, GFP_KERNEL);
23418c2ecf20Sopenharmony_ci		if (!pp->irq_desc) {
23428c2ecf20Sopenharmony_ci			devm_kfree(dev, pp);
23438c2ecf20Sopenharmony_ci			return -ENOMEM;
23448c2ecf20Sopenharmony_ci		}
23458c2ecf20Sopenharmony_ci		snprintf(pp->irq_desc, 8,
23468c2ecf20Sopenharmony_ci			 "%s%d", dev_driver_string(dev), ap->port_no);
23478c2ecf20Sopenharmony_ci	}
23488c2ecf20Sopenharmony_ci
23498c2ecf20Sopenharmony_ci	/* check FBS capability */
23508c2ecf20Sopenharmony_ci	if ((hpriv->cap & HOST_CAP_FBS) && sata_pmp_supported(ap)) {
23518c2ecf20Sopenharmony_ci		void __iomem *port_mmio = ahci_port_base(ap);
23528c2ecf20Sopenharmony_ci		u32 cmd = readl(port_mmio + PORT_CMD);
23538c2ecf20Sopenharmony_ci		if (cmd & PORT_CMD_FBSCP)
23548c2ecf20Sopenharmony_ci			pp->fbs_supported = true;
23558c2ecf20Sopenharmony_ci		else if (hpriv->flags & AHCI_HFLAG_YES_FBS) {
23568c2ecf20Sopenharmony_ci			dev_info(dev, "port %d can do FBS, forcing FBSCP\n",
23578c2ecf20Sopenharmony_ci				 ap->port_no);
23588c2ecf20Sopenharmony_ci			pp->fbs_supported = true;
23598c2ecf20Sopenharmony_ci		} else
23608c2ecf20Sopenharmony_ci			dev_warn(dev, "port %d is not capable of FBS\n",
23618c2ecf20Sopenharmony_ci				 ap->port_no);
23628c2ecf20Sopenharmony_ci	}
23638c2ecf20Sopenharmony_ci
23648c2ecf20Sopenharmony_ci	if (pp->fbs_supported) {
23658c2ecf20Sopenharmony_ci		dma_sz = AHCI_PORT_PRIV_FBS_DMA_SZ;
23668c2ecf20Sopenharmony_ci		rx_fis_sz = AHCI_RX_FIS_SZ * 16;
23678c2ecf20Sopenharmony_ci	} else {
23688c2ecf20Sopenharmony_ci		dma_sz = AHCI_PORT_PRIV_DMA_SZ;
23698c2ecf20Sopenharmony_ci		rx_fis_sz = AHCI_RX_FIS_SZ;
23708c2ecf20Sopenharmony_ci	}
23718c2ecf20Sopenharmony_ci
23728c2ecf20Sopenharmony_ci	mem = dmam_alloc_coherent(dev, dma_sz, &mem_dma, GFP_KERNEL);
23738c2ecf20Sopenharmony_ci	if (!mem)
23748c2ecf20Sopenharmony_ci		return -ENOMEM;
23758c2ecf20Sopenharmony_ci
23768c2ecf20Sopenharmony_ci	/*
23778c2ecf20Sopenharmony_ci	 * First item in chunk of DMA memory: 32-slot command table,
23788c2ecf20Sopenharmony_ci	 * 32 bytes each in size
23798c2ecf20Sopenharmony_ci	 */
23808c2ecf20Sopenharmony_ci	pp->cmd_slot = mem;
23818c2ecf20Sopenharmony_ci	pp->cmd_slot_dma = mem_dma;
23828c2ecf20Sopenharmony_ci
23838c2ecf20Sopenharmony_ci	mem += AHCI_CMD_SLOT_SZ;
23848c2ecf20Sopenharmony_ci	mem_dma += AHCI_CMD_SLOT_SZ;
23858c2ecf20Sopenharmony_ci
23868c2ecf20Sopenharmony_ci	/*
23878c2ecf20Sopenharmony_ci	 * Second item: Received-FIS area
23888c2ecf20Sopenharmony_ci	 */
23898c2ecf20Sopenharmony_ci	pp->rx_fis = mem;
23908c2ecf20Sopenharmony_ci	pp->rx_fis_dma = mem_dma;
23918c2ecf20Sopenharmony_ci
23928c2ecf20Sopenharmony_ci	mem += rx_fis_sz;
23938c2ecf20Sopenharmony_ci	mem_dma += rx_fis_sz;
23948c2ecf20Sopenharmony_ci
23958c2ecf20Sopenharmony_ci	/*
23968c2ecf20Sopenharmony_ci	 * Third item: data area for storing a single command
23978c2ecf20Sopenharmony_ci	 * and its scatter-gather table
23988c2ecf20Sopenharmony_ci	 */
23998c2ecf20Sopenharmony_ci	pp->cmd_tbl = mem;
24008c2ecf20Sopenharmony_ci	pp->cmd_tbl_dma = mem_dma;
24018c2ecf20Sopenharmony_ci
24028c2ecf20Sopenharmony_ci	/*
24038c2ecf20Sopenharmony_ci	 * Save off initial list of interrupts to be enabled.
24048c2ecf20Sopenharmony_ci	 * This could be changed later
24058c2ecf20Sopenharmony_ci	 */
24068c2ecf20Sopenharmony_ci	pp->intr_mask = DEF_PORT_IRQ;
24078c2ecf20Sopenharmony_ci
24088c2ecf20Sopenharmony_ci	/*
24098c2ecf20Sopenharmony_ci	 * Switch to per-port locking in case each port has its own MSI vector.
24108c2ecf20Sopenharmony_ci	 */
24118c2ecf20Sopenharmony_ci	if (hpriv->flags & AHCI_HFLAG_MULTI_MSI) {
24128c2ecf20Sopenharmony_ci		spin_lock_init(&pp->lock);
24138c2ecf20Sopenharmony_ci		ap->lock = &pp->lock;
24148c2ecf20Sopenharmony_ci	}
24158c2ecf20Sopenharmony_ci
24168c2ecf20Sopenharmony_ci	ap->private_data = pp;
24178c2ecf20Sopenharmony_ci
24188c2ecf20Sopenharmony_ci	/* engage engines, captain */
24198c2ecf20Sopenharmony_ci	return ahci_port_resume(ap);
24208c2ecf20Sopenharmony_ci}
24218c2ecf20Sopenharmony_ci
24228c2ecf20Sopenharmony_cistatic void ahci_port_stop(struct ata_port *ap)
24238c2ecf20Sopenharmony_ci{
24248c2ecf20Sopenharmony_ci	const char *emsg = NULL;
24258c2ecf20Sopenharmony_ci	struct ahci_host_priv *hpriv = ap->host->private_data;
24268c2ecf20Sopenharmony_ci	void __iomem *host_mmio = hpriv->mmio;
24278c2ecf20Sopenharmony_ci	int rc;
24288c2ecf20Sopenharmony_ci
24298c2ecf20Sopenharmony_ci	/* de-initialize port */
24308c2ecf20Sopenharmony_ci	rc = ahci_deinit_port(ap, &emsg);
24318c2ecf20Sopenharmony_ci	if (rc)
24328c2ecf20Sopenharmony_ci		ata_port_warn(ap, "%s (%d)\n", emsg, rc);
24338c2ecf20Sopenharmony_ci
24348c2ecf20Sopenharmony_ci	/*
24358c2ecf20Sopenharmony_ci	 * Clear GHC.IS to prevent stuck INTx after disabling MSI and
24368c2ecf20Sopenharmony_ci	 * re-enabling INTx.
24378c2ecf20Sopenharmony_ci	 */
24388c2ecf20Sopenharmony_ci	writel(1 << ap->port_no, host_mmio + HOST_IRQ_STAT);
24398c2ecf20Sopenharmony_ci
24408c2ecf20Sopenharmony_ci	ahci_rpm_put_port(ap);
24418c2ecf20Sopenharmony_ci}
24428c2ecf20Sopenharmony_ci
24438c2ecf20Sopenharmony_civoid ahci_print_info(struct ata_host *host, const char *scc_s)
24448c2ecf20Sopenharmony_ci{
24458c2ecf20Sopenharmony_ci	struct ahci_host_priv *hpriv = host->private_data;
24468c2ecf20Sopenharmony_ci	u32 vers, cap, cap2, impl, speed;
24478c2ecf20Sopenharmony_ci	const char *speed_s;
24488c2ecf20Sopenharmony_ci
24498c2ecf20Sopenharmony_ci	vers = hpriv->version;
24508c2ecf20Sopenharmony_ci	cap = hpriv->cap;
24518c2ecf20Sopenharmony_ci	cap2 = hpriv->cap2;
24528c2ecf20Sopenharmony_ci	impl = hpriv->port_map;
24538c2ecf20Sopenharmony_ci
24548c2ecf20Sopenharmony_ci	speed = (cap >> 20) & 0xf;
24558c2ecf20Sopenharmony_ci	if (speed == 1)
24568c2ecf20Sopenharmony_ci		speed_s = "1.5";
24578c2ecf20Sopenharmony_ci	else if (speed == 2)
24588c2ecf20Sopenharmony_ci		speed_s = "3";
24598c2ecf20Sopenharmony_ci	else if (speed == 3)
24608c2ecf20Sopenharmony_ci		speed_s = "6";
24618c2ecf20Sopenharmony_ci	else
24628c2ecf20Sopenharmony_ci		speed_s = "?";
24638c2ecf20Sopenharmony_ci
24648c2ecf20Sopenharmony_ci	dev_info(host->dev,
24658c2ecf20Sopenharmony_ci		"AHCI %02x%02x.%02x%02x "
24668c2ecf20Sopenharmony_ci		"%u slots %u ports %s Gbps 0x%x impl %s mode\n"
24678c2ecf20Sopenharmony_ci		,
24688c2ecf20Sopenharmony_ci
24698c2ecf20Sopenharmony_ci		(vers >> 24) & 0xff,
24708c2ecf20Sopenharmony_ci		(vers >> 16) & 0xff,
24718c2ecf20Sopenharmony_ci		(vers >> 8) & 0xff,
24728c2ecf20Sopenharmony_ci		vers & 0xff,
24738c2ecf20Sopenharmony_ci
24748c2ecf20Sopenharmony_ci		((cap >> 8) & 0x1f) + 1,
24758c2ecf20Sopenharmony_ci		(cap & 0x1f) + 1,
24768c2ecf20Sopenharmony_ci		speed_s,
24778c2ecf20Sopenharmony_ci		impl,
24788c2ecf20Sopenharmony_ci		scc_s);
24798c2ecf20Sopenharmony_ci
24808c2ecf20Sopenharmony_ci	dev_info(host->dev,
24818c2ecf20Sopenharmony_ci		"flags: "
24828c2ecf20Sopenharmony_ci		"%s%s%s%s%s%s%s"
24838c2ecf20Sopenharmony_ci		"%s%s%s%s%s%s%s"
24848c2ecf20Sopenharmony_ci		"%s%s%s%s%s%s%s"
24858c2ecf20Sopenharmony_ci		"%s%s\n"
24868c2ecf20Sopenharmony_ci		,
24878c2ecf20Sopenharmony_ci
24888c2ecf20Sopenharmony_ci		cap & HOST_CAP_64 ? "64bit " : "",
24898c2ecf20Sopenharmony_ci		cap & HOST_CAP_NCQ ? "ncq " : "",
24908c2ecf20Sopenharmony_ci		cap & HOST_CAP_SNTF ? "sntf " : "",
24918c2ecf20Sopenharmony_ci		cap & HOST_CAP_MPS ? "ilck " : "",
24928c2ecf20Sopenharmony_ci		cap & HOST_CAP_SSS ? "stag " : "",
24938c2ecf20Sopenharmony_ci		cap & HOST_CAP_ALPM ? "pm " : "",
24948c2ecf20Sopenharmony_ci		cap & HOST_CAP_LED ? "led " : "",
24958c2ecf20Sopenharmony_ci		cap & HOST_CAP_CLO ? "clo " : "",
24968c2ecf20Sopenharmony_ci		cap & HOST_CAP_ONLY ? "only " : "",
24978c2ecf20Sopenharmony_ci		cap & HOST_CAP_PMP ? "pmp " : "",
24988c2ecf20Sopenharmony_ci		cap & HOST_CAP_FBS ? "fbs " : "",
24998c2ecf20Sopenharmony_ci		cap & HOST_CAP_PIO_MULTI ? "pio " : "",
25008c2ecf20Sopenharmony_ci		cap & HOST_CAP_SSC ? "slum " : "",
25018c2ecf20Sopenharmony_ci		cap & HOST_CAP_PART ? "part " : "",
25028c2ecf20Sopenharmony_ci		cap & HOST_CAP_CCC ? "ccc " : "",
25038c2ecf20Sopenharmony_ci		cap & HOST_CAP_EMS ? "ems " : "",
25048c2ecf20Sopenharmony_ci		cap & HOST_CAP_SXS ? "sxs " : "",
25058c2ecf20Sopenharmony_ci		cap2 & HOST_CAP2_DESO ? "deso " : "",
25068c2ecf20Sopenharmony_ci		cap2 & HOST_CAP2_SADM ? "sadm " : "",
25078c2ecf20Sopenharmony_ci		cap2 & HOST_CAP2_SDS ? "sds " : "",
25088c2ecf20Sopenharmony_ci		cap2 & HOST_CAP2_APST ? "apst " : "",
25098c2ecf20Sopenharmony_ci		cap2 & HOST_CAP2_NVMHCI ? "nvmp " : "",
25108c2ecf20Sopenharmony_ci		cap2 & HOST_CAP2_BOH ? "boh " : ""
25118c2ecf20Sopenharmony_ci		);
25128c2ecf20Sopenharmony_ci}
25138c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ahci_print_info);
25148c2ecf20Sopenharmony_ci
25158c2ecf20Sopenharmony_civoid ahci_set_em_messages(struct ahci_host_priv *hpriv,
25168c2ecf20Sopenharmony_ci			  struct ata_port_info *pi)
25178c2ecf20Sopenharmony_ci{
25188c2ecf20Sopenharmony_ci	u8 messages;
25198c2ecf20Sopenharmony_ci	void __iomem *mmio = hpriv->mmio;
25208c2ecf20Sopenharmony_ci	u32 em_loc = readl(mmio + HOST_EM_LOC);
25218c2ecf20Sopenharmony_ci	u32 em_ctl = readl(mmio + HOST_EM_CTL);
25228c2ecf20Sopenharmony_ci
25238c2ecf20Sopenharmony_ci	if (!ahci_em_messages || !(hpriv->cap & HOST_CAP_EMS))
25248c2ecf20Sopenharmony_ci		return;
25258c2ecf20Sopenharmony_ci
25268c2ecf20Sopenharmony_ci	messages = (em_ctl & EM_CTRL_MSG_TYPE) >> 16;
25278c2ecf20Sopenharmony_ci
25288c2ecf20Sopenharmony_ci	if (messages) {
25298c2ecf20Sopenharmony_ci		/* store em_loc */
25308c2ecf20Sopenharmony_ci		hpriv->em_loc = ((em_loc >> 16) * 4);
25318c2ecf20Sopenharmony_ci		hpriv->em_buf_sz = ((em_loc & 0xff) * 4);
25328c2ecf20Sopenharmony_ci		hpriv->em_msg_type = messages;
25338c2ecf20Sopenharmony_ci		pi->flags |= ATA_FLAG_EM;
25348c2ecf20Sopenharmony_ci		if (!(em_ctl & EM_CTL_ALHD))
25358c2ecf20Sopenharmony_ci			pi->flags |= ATA_FLAG_SW_ACTIVITY;
25368c2ecf20Sopenharmony_ci	}
25378c2ecf20Sopenharmony_ci}
25388c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ahci_set_em_messages);
25398c2ecf20Sopenharmony_ci
25408c2ecf20Sopenharmony_cistatic int ahci_host_activate_multi_irqs(struct ata_host *host,
25418c2ecf20Sopenharmony_ci					 struct scsi_host_template *sht)
25428c2ecf20Sopenharmony_ci{
25438c2ecf20Sopenharmony_ci	struct ahci_host_priv *hpriv = host->private_data;
25448c2ecf20Sopenharmony_ci	int i, rc;
25458c2ecf20Sopenharmony_ci
25468c2ecf20Sopenharmony_ci	rc = ata_host_start(host);
25478c2ecf20Sopenharmony_ci	if (rc)
25488c2ecf20Sopenharmony_ci		return rc;
25498c2ecf20Sopenharmony_ci	/*
25508c2ecf20Sopenharmony_ci	 * Requests IRQs according to AHCI-1.1 when multiple MSIs were
25518c2ecf20Sopenharmony_ci	 * allocated. That is one MSI per port, starting from @irq.
25528c2ecf20Sopenharmony_ci	 */
25538c2ecf20Sopenharmony_ci	for (i = 0; i < host->n_ports; i++) {
25548c2ecf20Sopenharmony_ci		struct ahci_port_priv *pp = host->ports[i]->private_data;
25558c2ecf20Sopenharmony_ci		int irq = hpriv->get_irq_vector(host, i);
25568c2ecf20Sopenharmony_ci
25578c2ecf20Sopenharmony_ci		/* Do not receive interrupts sent by dummy ports */
25588c2ecf20Sopenharmony_ci		if (!pp) {
25598c2ecf20Sopenharmony_ci			disable_irq(irq);
25608c2ecf20Sopenharmony_ci			continue;
25618c2ecf20Sopenharmony_ci		}
25628c2ecf20Sopenharmony_ci
25638c2ecf20Sopenharmony_ci		rc = devm_request_irq(host->dev, irq, ahci_multi_irqs_intr_hard,
25648c2ecf20Sopenharmony_ci				0, pp->irq_desc, host->ports[i]);
25658c2ecf20Sopenharmony_ci
25668c2ecf20Sopenharmony_ci		if (rc)
25678c2ecf20Sopenharmony_ci			return rc;
25688c2ecf20Sopenharmony_ci		ata_port_desc(host->ports[i], "irq %d", irq);
25698c2ecf20Sopenharmony_ci	}
25708c2ecf20Sopenharmony_ci
25718c2ecf20Sopenharmony_ci	return ata_host_register(host, sht);
25728c2ecf20Sopenharmony_ci}
25738c2ecf20Sopenharmony_ci
25748c2ecf20Sopenharmony_ci/**
25758c2ecf20Sopenharmony_ci *	ahci_host_activate - start AHCI host, request IRQs and register it
25768c2ecf20Sopenharmony_ci *	@host: target ATA host
25778c2ecf20Sopenharmony_ci *	@sht: scsi_host_template to use when registering the host
25788c2ecf20Sopenharmony_ci *
25798c2ecf20Sopenharmony_ci *	LOCKING:
25808c2ecf20Sopenharmony_ci *	Inherited from calling layer (may sleep).
25818c2ecf20Sopenharmony_ci *
25828c2ecf20Sopenharmony_ci *	RETURNS:
25838c2ecf20Sopenharmony_ci *	0 on success, -errno otherwise.
25848c2ecf20Sopenharmony_ci */
25858c2ecf20Sopenharmony_ciint ahci_host_activate(struct ata_host *host, struct scsi_host_template *sht)
25868c2ecf20Sopenharmony_ci{
25878c2ecf20Sopenharmony_ci	struct ahci_host_priv *hpriv = host->private_data;
25888c2ecf20Sopenharmony_ci	int irq = hpriv->irq;
25898c2ecf20Sopenharmony_ci	int rc;
25908c2ecf20Sopenharmony_ci
25918c2ecf20Sopenharmony_ci	if (hpriv->flags & AHCI_HFLAG_MULTI_MSI) {
25928c2ecf20Sopenharmony_ci		if (hpriv->irq_handler &&
25938c2ecf20Sopenharmony_ci		    hpriv->irq_handler != ahci_single_level_irq_intr)
25948c2ecf20Sopenharmony_ci			dev_warn(host->dev,
25958c2ecf20Sopenharmony_ci			         "both AHCI_HFLAG_MULTI_MSI flag set and custom irq handler implemented\n");
25968c2ecf20Sopenharmony_ci		if (!hpriv->get_irq_vector) {
25978c2ecf20Sopenharmony_ci			dev_err(host->dev,
25988c2ecf20Sopenharmony_ci				"AHCI_HFLAG_MULTI_MSI requires ->get_irq_vector!\n");
25998c2ecf20Sopenharmony_ci			return -EIO;
26008c2ecf20Sopenharmony_ci		}
26018c2ecf20Sopenharmony_ci
26028c2ecf20Sopenharmony_ci		rc = ahci_host_activate_multi_irqs(host, sht);
26038c2ecf20Sopenharmony_ci	} else {
26048c2ecf20Sopenharmony_ci		rc = ata_host_activate(host, irq, hpriv->irq_handler,
26058c2ecf20Sopenharmony_ci				       IRQF_SHARED, sht);
26068c2ecf20Sopenharmony_ci	}
26078c2ecf20Sopenharmony_ci
26088c2ecf20Sopenharmony_ci
26098c2ecf20Sopenharmony_ci	return rc;
26108c2ecf20Sopenharmony_ci}
26118c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ahci_host_activate);
26128c2ecf20Sopenharmony_ci
26138c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jeff Garzik");
26148c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Common AHCI SATA low-level routines");
26158c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
2616