diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index e377a065d491f54a0c3ba673a4a9892e40fe07eb..e7b931eb80500694ae3e33f6fb5c6f414526c95d 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -1784,6 +1784,108 @@ static void ahci_intel_pcs_quirk(struct pci_dev *pdev, struct ahci_host_priv *hp } } +#ifdef CONFIG_CPU_SUP_ZHAOXIN || CONFIG_CPU_SUP_CENTAUR +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_init_quirk(struct pci_dev *pdev, struct ahci_host_priv *hpriv) { } +static inline void ahci_zx_led_remove_quirk(struct pci_dev *pdev) { } +#endif + static ssize_t remapped_nvme_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -1918,6 +2020,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; @@ -2069,6 +2173,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 df8f8a1a3a34c3ee26d0d2b899522a82d220b6c2..c700d07ae7a1b779e31b45a8a617b0830f0920b9 100644 --- a/drivers/ata/ahci.h +++ b/drivers/ata/ahci.h @@ -36,6 +36,10 @@ #define EM_MSG_LED_VALUE_ACTIVITY 0x00070000 #define EM_MSG_LED_VALUE_OFF 0xfff80000 #define EM_MSG_LED_VALUE_ON 0x00010000 +#ifdef CONFIG_CPU_SUP_ZHAOXIN || CONFIG_CPU_SUP_CENTAUR +/* fix zhaoxin Enclosure Management quirk */ +#define ZX_GET_BUS_NUMBER_QUIRK 0x000012B0 +#endif /* CONFIG_CPU_SUP_ZHAOXIN || CONFIG_CPU_SUP_CENTAUR */ enum { AHCI_MAX_PORTS = 32, @@ -379,6 +383,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 3bd0d82f4c2a93cc981e8ec4d315e8266bae8d0d..f5502590b9977c09db11bc5976d9bc03026ddf66 100644 --- a/drivers/ata/libahci.c +++ b/drivers/ata/libahci.c @@ -367,6 +367,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_CPU_SUP_ZHAOXIN || CONFIG_CPU_SUP_CENTAUR + 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) @@ -389,7 +401,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); @@ -1138,13 +1150,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 @@ -1162,6 +1175,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); }