From: Miao Wang via B4 Relay <devnull+shankerwangmiao.gmail.com@kernel.org>
To: Binbin Zhou <zhoubinbin@loongson.cn>,
Chong Qiao <qiaochong@loongson.cn>, Lee Jones <lee@kernel.org>,
Huacai Chen <chenhuacai@kernel.org>,
Corey Minyard <corey@minyard.net>,
Linus Walleij <linusw@kernel.org>,
Bartosz Golaszewski <brgl@kernel.org>
Cc: Xi Ruoyao <xry111@xry111.site>, WANG Xuerui <kernel@xen0n.name>,
Yinbo Zhu <zhuyinbo@loongson.cn>,
Jiaxun Yang <jiaxun.yang@flygoat.com>,
mfd@lists.linux.dev, linux-kernel@vger.kernel.org,
linux-gpio@vger.kernel.org,
Miao Wang <shankerwangmiao@gmail.com>
Subject: [PATCH RFC 5/5] mfd: ls2kbmc: capture the reset event of BMC through GPIO
Date: Wed, 08 Jul 2026 03:24:06 +0800 [thread overview]
Message-ID: <20260708-ls2kbmc-mod-v1-5-c344bf5defa3@gmail.com> (raw)
In-Reply-To: <20260708-ls2kbmc-mod-v1-0-c344bf5defa3@gmail.com>
From: Miao Wang <shankerwangmiao@gmail.com>
The reset event of BMC is captured through GPIO. However, this driver
bypasses the GPIO framework and directly accesses the GPIO controller
through the fixed address. When the same GPIO controller is also
exposed through ACPI and probed by the corresponding GPIO driver,
there would be a conflict between the two drivers.
This patch will try to find the GPIO through declared GPIO pin in the
_CRS resources of the ACPI node. If no such delaration is found, the
driver will fall back to search for the correct GPIO controller and pin
according to the fixed address and pin number. A possible DSDT
declaration for the GPIO pin might be as follows:
Device (BMC0) {
Name (_ADR, ...) // Match the PCI address of the BMC device
// \_SB.GPO1 is the ACPI path of the GPIO controller
Name (_CRS, ResourceTemplate () {
GpioInt (Edge, ActiveLow, Exclusive, PullNone, 0,
"\\_SB.GPO1", 0) {
14 // 14 is the GPIO pin number
}
}
Signed-off-by: Miao Wang <shankerwangmiao@gmail.com>
---
drivers/mfd/ls2k-bmc-core.c | 162 +++++++++++++++++++++++++++++++-------------
1 file changed, 115 insertions(+), 47 deletions(-)
diff --git a/drivers/mfd/ls2k-bmc-core.c b/drivers/mfd/ls2k-bmc-core.c
index 4ceae6c70fb27ae2d6a2a92f4f45a376f1cdb699..5005908802766cf855bc48a4d7d73495dea16e9c 100644
--- a/drivers/mfd/ls2k-bmc-core.c
+++ b/drivers/mfd/ls2k-bmc-core.c
@@ -26,6 +26,9 @@
#include <linux/stop_machine.h>
#include <linux/vt_kern.h>
#include <linux/console.h>
+#include <linux/gpio/consumer.h>
+#include <linux/gpio/driver.h>
+#include <linux/gpio.h>
/* LS2K BMC resources */
#define LS2K_DISPLAY_RES_START (SZ_16M + SZ_2M)
@@ -81,18 +84,6 @@
#define PCI_REG_STRIDE 0x4
-#define LS2K_BMC_RESET_GPIO 14
-#define LOONGSON_GPIO_REG_BASE 0x1FE00500
-#define LOONGSON_GPIO_REG_SIZE 0x18
-#define LOONGSON_GPIO_OEN 0x0
-#define LOONGSON_GPIO_FUNC 0x4
-#define LOONGSON_GPIO_INTPOL 0x10
-#define LOONGSON_GPIO_INTEN 0x14
-
-#define LOONGSON_IO_INT_BASE 16
-#define LS2K_BMC_RESET_GPIO_INT_VEC (LS2K_BMC_RESET_GPIO % 8)
-#define LS2K_BMC_RESET_GPIO_GSI (LOONGSON_IO_INT_BASE + LS2K_BMC_RESET_GPIO_INT_VEC)
-
enum {
LS2K_BMC_DISPLAY,
LS2K_BMC_IPMI0,
@@ -186,6 +177,7 @@ struct ls2k_bmc_ddata {
struct work_struct bmc_reset_work;
struct ls2k_bmc_pci_data bmc_pci_data;
struct ls2k_bmc_bridge_pci_data bridge_pci_data;
+ struct gpio_desc *reset_gpio;
};
static bool ls2k_bmc_bar0_addr_is_set(struct pci_dev *pdev)
@@ -334,6 +326,22 @@ static irqreturn_t ls2k_bmc_interrupt(int irq, void *arg)
return IRQ_HANDLED;
}
+static irqreturn_t ls2k_bmc_gpio_interrupt(int irq, void *arg)
+{
+ struct ls2k_bmc_ddata *ddata = arg;
+
+ int val = gpiod_get_raw_value(ddata->reset_gpio);
+
+ /*
+ * The GPIO is active low, so when the value is 0,
+ * it indicates a reset event.
+ */
+ if (val == 0)
+ return ls2k_bmc_interrupt(irq, arg);
+
+ return IRQ_NONE;
+}
+
/*
* Saves the BMC parent device (LS7A) and its own PCI configuration space registers
* that need to be restored after BMC reset.
@@ -375,11 +383,83 @@ static void ls2k_bmc_save_pci_data(struct pci_dev *pdev, struct ls2k_bmc_ddata *
pci_read_config_dword(pdev, PCI_INTERRUPT_LINE, &ddata->bmc_pci_data.interrupt_line);
}
+static int ls2k_bmc_gpiochip_find(struct gpio_chip *gc, const void *data)
+{
+ struct acpi_device *adev;
+ struct list_head resource_list;
+ struct resource_entry *rentry;
+ struct fwnode_handle *fwnode = gc->fwnode ?: gc->parent->fwnode;
+ phys_addr_t start_addr = (phys_addr_t) data;
+ int ret, found = 0;
+
+ if (!is_acpi_node(fwnode))
+ goto out;
+
+ adev = to_acpi_device_node(fwnode);
+ if (!adev)
+ goto out;
+
+ INIT_LIST_HEAD(&resource_list);
+
+ ret = acpi_dev_get_memory_resources(adev, &resource_list);
+ if (ret < 0)
+ goto out;
+ rentry = list_first_entry_or_null(&resource_list, struct resource_entry, node);
+ if (!rentry)
+ goto free_resource_list;
+ if (rentry->res->start == start_addr)
+ found = 1;
+
+free_resource_list:
+ acpi_dev_free_resource_list(&resource_list);
+out:
+ return found;
+}
+
+static struct gpio_desc *ls2k_bmc_find_gpio(struct ls2k_bmc_ddata *ddata)
+{
+ /*
+ * In conventional way, the GPIO should be obtained through ACPI or
+ * device tree. However, when the information is not available,
+ * we should find the GPIO according to the convention of the server
+ * boards with LS2K BMC, the gpio signal relelecting the reset event
+ * of the BMC should be connected to pin 14 of the GPIO input of
+ * the first CPU node. The address of that GPIO controller is fixed.
+ */
+ static const phys_addr_t LOONGSON_GPIO_REG_BASE = 0x1FE00500;
+ static const unsigned int LS2K_BMC_RESET_GPIO = 14;
+ struct gpio_desc *desc;
+ unsigned int legacy_gpio;
+ int ret;
+ struct gpio_device *gdev __free(gpio_device_put) = NULL;
+
+ dev_dbg(ddata->dev, "Searching for GPIO chip at address %pa\n", &LOONGSON_GPIO_REG_BASE);
+
+ gdev = gpio_device_find((void *)LOONGSON_GPIO_REG_BASE, ls2k_bmc_gpiochip_find);
+
+ if (!gdev) {
+ dev_dbg(ddata->dev, "cannot find GPIO chip at address %pa, deferring\n",
+ &LOONGSON_GPIO_REG_BASE);
+ return ERR_PTR(-EPROBE_DEFER);
+ }
+
+ desc = gpio_device_get_desc(gdev, LS2K_BMC_RESET_GPIO);
+ if (IS_ERR(desc))
+ return desc;
+
+ // XXX: might be better to use gpiod_request()
+ legacy_gpio = desc_to_gpio(desc);
+ ret = devm_gpio_request_one(ddata->dev, legacy_gpio, GPIOF_IN, dev_name(ddata->dev));
+ if (ret < 0)
+ return ERR_PTR(ret);
+
+ return gpio_to_desc(legacy_gpio);
+}
+
static int ls2k_bmc_init(struct ls2k_bmc_ddata *ddata)
{
struct pci_dev *pdev = to_pci_dev(ddata->dev);
- void __iomem *gpio_base;
- int gpio_irq, ret, val;
+ int gpio_irq, ret;
ls2k_bmc_save_pci_data(pdev, ddata);
@@ -392,44 +472,32 @@ static int ls2k_bmc_init(struct ls2k_bmc_ddata *ddata)
return ret;
}
- gpio_base = ioremap(LOONGSON_GPIO_REG_BASE, LOONGSON_GPIO_REG_SIZE);
- if (!gpio_base)
- return -ENOMEM;
-
- /* Disable GPIO output */
- val = readl(gpio_base + LOONGSON_GPIO_OEN);
- writel(val | BIT(LS2K_BMC_RESET_GPIO), gpio_base + LOONGSON_GPIO_OEN);
-
- /* Enable GPIO functionality */
- val = readl(gpio_base + LOONGSON_GPIO_FUNC);
- writel(val & ~BIT(LS2K_BMC_RESET_GPIO), gpio_base + LOONGSON_GPIO_FUNC);
-
- /* Set GPIO interrupts to low-level active */
- val = readl(gpio_base + LOONGSON_GPIO_INTPOL);
- writel(val & ~BIT(LS2K_BMC_RESET_GPIO), gpio_base + LOONGSON_GPIO_INTPOL);
-
- /* Enable GPIO interrupts */
- val = readl(gpio_base + LOONGSON_GPIO_INTEN);
- writel(val | BIT(LS2K_BMC_RESET_GPIO), gpio_base + LOONGSON_GPIO_INTEN);
+ ddata->reset_gpio = devm_gpiod_get_index_optional(&pdev->dev, NULL, 0, GPIOD_IN);
+ if (IS_ERR(ddata->reset_gpio)) {
+ ret = PTR_ERR(ddata->reset_gpio);
+ ddata->reset_gpio = NULL;
+ return dev_err_probe(ddata->dev, ret, "Failed to get reset GPIO\n");
+ }
+ if (ddata->reset_gpio == NULL) {
+ ddata->reset_gpio = ls2k_bmc_find_gpio(ddata);
+ if (IS_ERR(ddata->reset_gpio)) {
+ ret = PTR_ERR(ddata->reset_gpio);
+ ddata->reset_gpio = NULL;
+ return dev_err_probe(ddata->dev, ret, "Failed to find reset GPIO\n");
+ }
+ }
- iounmap(gpio_base);
+ gpio_irq = gpiod_to_irq(ddata->reset_gpio);
- /*
- * Since gpio_chip->to_irq is not implemented in the Loongson-3 GPIO driver,
- * acpi_register_gsi() is used to obtain the GPIO IRQ. The GPIO interrupt is a
- * watchdog interrupt that is triggered when the BMC resets.
- */
- gpio_irq = acpi_register_gsi(NULL, LS2K_BMC_RESET_GPIO_GSI, ACPI_EDGE_SENSITIVE,
- ACPI_ACTIVE_LOW);
if (gpio_irq < 0)
- return gpio_irq;
+ return dev_err_probe(ddata->dev, gpio_irq, "Failed to get GPIO IRQ\n");
- ret = devm_request_irq(ddata->dev, gpio_irq, ls2k_bmc_interrupt,
- IRQF_SHARED | IRQF_TRIGGER_FALLING, "ls2kbmc gpio", ddata);
- if (ret)
- dev_err(ddata->dev, "Failed to request LS2KBMC GPIO IRQ %d.\n", gpio_irq);
+ ret = devm_request_irq(&pdev->dev, gpio_irq, ls2k_bmc_gpio_interrupt,
+ IRQF_SHARED | IRQF_TRIGGER_FALLING, "ls2kbmc reset", ddata);
+
+ if (ret != 0)
+ return dev_err_probe(ddata->dev, ret, "Failed to request GPIO IRQ %d.\n", gpio_irq);
- acpi_unregister_gsi(LS2K_BMC_RESET_GPIO_GSI);
return ret;
}
--
2.49.0
prev parent reply other threads:[~2026-07-07 19:24 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-07 19:24 [PATCH RFC 0/5] mfd: ls2kbmc: multiple fixes for this driver Miao Wang via B4 Relay
2026-07-07 19:24 ` [PATCH RFC 1/5] mfd: ls2kbmc: make a copy when parsing mode string Miao Wang via B4 Relay
2026-07-07 19:24 ` [PATCH RFC 2/5] mfd: ls2kbmc: sanity check for the connected pci port Miao Wang via B4 Relay
2026-07-07 19:24 ` [PATCH RFC 3/5] mfd: ls2kbmc: redraw using exported functions Miao Wang via B4 Relay
2026-07-07 19:24 ` [PATCH RFC 4/5] mfd: ls2kbmc: able to compile as a module Miao Wang via B4 Relay
2026-07-07 19:24 ` Miao Wang via B4 Relay [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260708-ls2kbmc-mod-v1-5-c344bf5defa3@gmail.com \
--to=devnull+shankerwangmiao.gmail.com@kernel.org \
--cc=brgl@kernel.org \
--cc=chenhuacai@kernel.org \
--cc=corey@minyard.net \
--cc=jiaxun.yang@flygoat.com \
--cc=kernel@xen0n.name \
--cc=lee@kernel.org \
--cc=linusw@kernel.org \
--cc=linux-gpio@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=mfd@lists.linux.dev \
--cc=qiaochong@loongson.cn \
--cc=shankerwangmiao@gmail.com \
--cc=xry111@xry111.site \
--cc=zhoubinbin@loongson.cn \
--cc=zhuyinbo@loongson.cn \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox