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