From 4079f0cd929c712f5f1e4cc7ccaa4fb72a547da4 Mon Sep 17 00:00:00 2001 From: leoliu-oc Date: Thu, 30 Oct 2025 11:15:15 +0800 Subject: [PATCH] anolis: ata: ahci: Fix Zhaoxin SATA LED quirk for KH-50000 ANBZ: #26827 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 | 17 +++++++ 3 files changed, 131 insertions(+) diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index 7b61915a141e..3f476cc93e4e 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -1793,6 +1793,108 @@ static ssize_t remapped_nvme_show(struct device *dev, static DEVICE_ATTR_RO(remapped_nvme); +#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 + static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) { unsigned int board_id = ent->driver_data; @@ -1915,6 +2017,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; @@ -2066,6 +2170,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..8ba831020d77 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_X86 +/*fix zhaoxin Enclosure Management quirk*/ +#define ZX_GET_BUS_NUMBER_QUIRK 0x000012B0 +#endif 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..eb7117e2117f 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_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) @@ -390,6 +402,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,6 +1151,8 @@ 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); @@ -1145,6 +1160,7 @@ static ssize_t ahci_transmit_led_message(struct ata_port *ap, u32 state, 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 +1178,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