diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index 5888a3d030bd328e513352949be05e9978c85019..72dcac6e727b4be66f03a8df0da48c2ecf130e72 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -1674,6 +1674,108 @@ static void ahci_intel_pcs_quirk(struct pci_dev *pdev, struct ahci_host_priv *hp } } +#ifdef CONFIG_X86 +static void ahci_zx_led_remove_quirk(struct pci_dev *pdev) +{ + struct ata_host *host = pci_get_drvdata(pdev); + struct ahci_host_priv *hpriv = host->private_data; + struct pci_dev *sata_pdev = NULL; + struct ata_host *sata_host = NULL; + struct ahci_host_priv *sata_hpriv = NULL; + void __iomem *p1_mmio_tmp = NULL; + struct pci_dev *target_p0_dev = NULL; + + if (!hpriv->px_index || !hpriv->has_p0_p1) + return; + + while ((sata_pdev = pci_get_device(PCI_VENDOR_ID_ZHAOXIN, 0x9083, sata_pdev)) != NULL) { + sata_host = pci_get_drvdata(sata_pdev); + sata_hpriv = sata_host ? sata_host->private_data : NULL; + if (!sata_hpriv) + continue; + if (sata_hpriv->sx_index == hpriv->sx_index) { + if (sata_hpriv->px_index == 1 && + PCI_FUNC(pdev->devfn) != PCI_FUNC(sata_pdev->devfn)) + p1_mmio_tmp = sata_hpriv->mmio; + else if (sata_hpriv->px_index == 0) + target_p0_dev = sata_pdev; + } + + if (target_p0_dev && p1_mmio_tmp) + break; + } + if (target_p0_dev) { + sata_host = pci_get_drvdata(target_p0_dev); + sata_hpriv = sata_host ? sata_host->private_data : NULL; + if (sata_hpriv) + sata_hpriv->p1_mmio = p1_mmio_tmp; + } +} + +static void ahci_zx_led_init_quirk(struct pci_dev *pdev, struct ahci_host_priv *hpriv) +{ + int i, err; + u8 p0_bus_number, p1_bus_number, target_px_index; + u64 val; + struct pci_dev *sata_pdev = NULL; + struct ata_host *sata_host = NULL; + struct ahci_host_priv *sata_hpriv = NULL; + + if (pdev->vendor != PCI_VENDOR_ID_ZHAOXIN || pdev->device != 0x9083 || + pdev->revision != 0x40) + return; + + val = native_read_msr_safe(ZX_GET_BUS_NUMBER_QUIRK, &err); + if (err) /* MSR read failed */ + return; + + hpriv->sx_index = 0xFF; + hpriv->px_index = 0xFF; + hpriv->p1_mmio = NULL; + hpriv->has_p0_p1 = false; + for (i = 0; i < 4; i++) { + p0_bus_number = val & 0xFF; + p1_bus_number = (val >> 8) & 0xFF; + if (pdev->bus->number == p0_bus_number) { + hpriv->sx_index = i; + hpriv->px_index = 0; + break; + } + if (pdev->bus->number == p1_bus_number) { + hpriv->sx_index = i; + hpriv->px_index = 1; + break; + } + val >>= 16; + } + /* Exit if no matching bus number found */ + if (hpriv->px_index == 0xFF || hpriv->sx_index == 0xFF) + return; + + target_px_index = !hpriv->px_index; + while ((sata_pdev = pci_get_device(PCI_VENDOR_ID_ZHAOXIN, 0x9083, sata_pdev)) != NULL) { + sata_host = pci_get_drvdata(sata_pdev); + sata_hpriv = sata_host ? sata_host->private_data : NULL; + if (!sata_hpriv) + continue; + + if (sata_hpriv->sx_index == hpriv->sx_index && + sata_hpriv->px_index == target_px_index) { + if (hpriv->px_index == 0) + hpriv->p1_mmio = sata_hpriv->mmio; + else + sata_hpriv->p1_mmio = hpriv->mmio; + hpriv->has_p0_p1 = true; + sata_hpriv->has_p0_p1 = true; + break; + } + } +} +#else +static inline void ahci_zx_led_remove_quirk(struct pci_dev *pdev) { } +static inline void ahci_zx_led_init_quirk(struct pci_dev *pdev, struct ahci_host_priv *hpriv) { } +#endif /* CONFIG_X86 */ + static ssize_t remapped_nvme_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -1810,6 +1912,8 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) /* save initial config */ ahci_pci_save_initial_config(pdev, hpriv); + ahci_zx_led_init_quirk(pdev, hpriv); + /* prepare host */ if (hpriv->cap & HOST_CAP_NCQ) { pi.flags |= ATA_FLAG_NCQ; @@ -1963,6 +2067,7 @@ static void ahci_shutdown_one(struct pci_dev *pdev) static void ahci_remove_one(struct pci_dev *pdev) { + ahci_zx_led_remove_quirk(pdev); sysfs_remove_file_from_group(&pdev->dev.kobj, &dev_attr_remapped_nvme.attr, NULL); diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h index 1ce89735699336fe237c089c4d959c8e8b7600fe..7bc545a1787bf695ee1babdb9e7a28f5170bc144 100644 --- a/drivers/ata/ahci.h +++ b/drivers/ata/ahci.h @@ -35,6 +35,10 @@ #define EM_MSG_LED_VALUE_ACTIVITY 0x00070000 #define EM_MSG_LED_VALUE_OFF 0xfff80000 #define EM_MSG_LED_VALUE_ON 0x00010000 +#ifdef CONFIG_X86 +/* fix zhaoxin Enclosure Management quirk */ +#define ZX_GET_BUS_NUMBER_QUIRK 0x000012B0 +#endif enum { AHCI_MAX_PORTS = 32, @@ -372,6 +376,11 @@ struct ahci_host_priv { /* only required for per-port MSI(-X) support */ int (*get_irq_vector)(struct ata_host *host, int port); + /* fix zhaoxin Enclosure Management quirk */ + void __iomem *p1_mmio; + u8 sx_index; + u8 px_index; + bool has_p0_p1; }; extern int ahci_ignore_sss; diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c index 389c23c208ecd70804679cac8ebbac31be933a62..1168b68433e49e53740f37231bd287164733b67f 100644 --- a/drivers/ata/libahci.c +++ b/drivers/ata/libahci.c @@ -345,6 +345,18 @@ static ssize_t ahci_read_em_buffer(struct device *dev, return i; } +static void __iomem *zx_led_get_mmio(struct ata_port *ap, struct ahci_host_priv *hpriv) +{ +#ifdef CONFIG_X86 + if (hpriv->has_p0_p1 && hpriv->px_index == 0) { + if (hpriv->p1_mmio) + return hpriv->p1_mmio; + dev_warn_ratelimited(ap->host->dev, "P1 removed, LED mode unavailable\n"); + } +#endif + return hpriv->mmio; +} + static ssize_t ahci_store_em_buffer(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) @@ -367,7 +379,7 @@ static ssize_t ahci_store_em_buffer(struct device *dev, ahci_rpm_get_port(ap); spin_lock_irqsave(ap->lock, flags); - + mmio = zx_led_get_mmio(ap, hpriv); em_ctl = readl(mmio + HOST_EM_CTL); if (em_ctl & EM_CTL_TM) { spin_unlock_irqrestore(ap->lock, flags); @@ -1081,13 +1093,14 @@ static ssize_t ahci_transmit_led_message(struct ata_port *ap, u32 state, * if we are still busy transmitting a previous message, * do not allow */ + mmio = zx_led_get_mmio(ap, hpriv); em_ctl = readl(mmio + HOST_EM_CTL); if (em_ctl & EM_CTL_TM) { spin_unlock_irqrestore(ap->lock, flags); ahci_rpm_put_port(ap); return -EBUSY; } - + mmio = hpriv->mmio; if (hpriv->em_msg_type & EM_MSG_TYPE_LED) { /* * create message header - this is all zero except for @@ -1105,6 +1118,7 @@ static ssize_t ahci_transmit_led_message(struct ata_port *ap, u32 state, /* * tell hardware to transmit the message */ + mmio = zx_led_get_mmio(ap, hpriv); writel(em_ctl | EM_CTL_TM, mmio + HOST_EM_CTL); }