From cf16f0c8b52cd6453f62bd03c035ff93b0c00f1e Mon Sep 17 00:00:00 2001 From: LeoLiu-oc Date: Mon, 20 Oct 2025 16:25:39 +0800 Subject: [PATCH] ata: ahci: Fix Zhaoxin SATA LED quirk for KH-50000 zhaoxin inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/ID2OP4 CVE: NA -------------------- This patch addresses a quirk in the SATA Enclosure Management (EM) LED functionality on Zhaoxin processors for the controller with device ID 0x9083 and revision 0x40. It ensures proper LED functionality by correctly managing memory-mapped I/O addresses and applying controller-specific quirks. Signed-off-by: LeoLiu-oc --- drivers/ata/ahci.c | 105 ++++++++++++++++++++++++++++++++++++++++++ drivers/ata/ahci.h | 9 ++++ drivers/ata/libahci.c | 18 +++++++- 3 files changed, 130 insertions(+), 2 deletions(-) diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index e377a065d491..e7b931eb8050 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 df8f8a1a3a34..c700d07ae7a1 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 3bd0d82f4c2a..f5502590b997 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); } -- Gitee