From: sashiko-bot@kernel.org
To: "Szuying Chen" <Chloe_Chen@asmedia.com.tw>, linux-gpio@vger.kernel.org
Subject: Re: [PATCH v3] gpio: amdptpci: add support for AMD PT PCI controller
Date: Wed, 28 Oct 2026 09:06:45 +0000 [thread overview]
Message-ID: <sashiko-outbox-187649@kernel.org> (raw)
In-Reply-To: <20261028084531.6237-1-Chloe_Chen@asmedia.com.tw>
Thank you for your contribution! Sashiko AI review found 9 potential issue(s) to consider:
- [High] gpio: amdptpci: missing runtime PM reference in pt27_irq_reqres()
- [High] gpio: amdptpci: MMIO access while runtime-suspended in pt27_gpio_isr()
- [High] gpio: amdptpci: pt27_irq_set_wake() accesses MMIO while runtime-suspended
- [Medium] gpio: amdptpci: invalid break and unlocked wake_mask in runtime suspend
- [Medium] gpio: amdptpci: fix devres ordering and PM cleanup in pt27_gpio_probe()
- [Medium] gpio: amdptpci: race on DIR and OUT registers due to wrong lock in PM callbacks
- [Medium] gpio: amdptpci: missing wake IRQ dispatch in pt27_gpio_resume() and wake_en gate
- [Low] gpio: amdptpci: missing space in struct pt27_gpio gen member declaration
- [Low] gpio: amdptpci: missing select GPIO_GENERIC and GPIOLIB_IRQCHIP in Kconfig
--
commit aad2d02577183448f2ddceeabe365624f8d6d152
Author: Szuying Chen <Chloe_Chen@asmedia.com.tw>
gpio: amdptpci: add support for AMD PT PCI controller
This commit adds the gpio-amdptpci driver to support the GPIO controller,
interrupt handling, and wakeup capabilities on AMD Promontory PCI
platforms.
> diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
> index 55e4280029d86..bcfb653a09804 100644
> --- a/drivers/gpio/Kconfig
> +++ b/drivers/gpio/Kconfig
> @@ -1890,6 +1890,12 @@ config GPIO_TC9563
> menu "PCI GPIO expanders"
> depends on PCI
>
> +config GPIO_AMDPTPCI
> + tristate "AMD Promontory PCI GPIO support"
> + depends on X86
> + help
> + Driver for Promontory IOHub PCI GPIO functionality.
[Severity: Low]
Should config GPIO_AMDPTPCI select GPIO_GENERIC and GPIOLIB_IRQCHIP?
In drivers/gpio/gpio-amdptpci.c, pt27_gpio_probe() unconditionally calls
gpio_generic_chip_init() (which requires CONFIG_GPIO_GENERIC) and sets up
gc->irq via gpio_irq_chip_set_chip() while the IRQ callbacks use
gpiochip_reqres_irq(), gpiochip_relres_irq(), gpiochip_enable_irq(), and
gpiochip_disable_irq() (which require CONFIG_GPIOLIB_IRQCHIP). Without
selecting those symbols, builds where no other enabled driver selects them
will fail at compile or link time.
> diff --git a/drivers/gpio/gpio-amdptpci.c b/drivers/gpio/gpio-amdptpci.c
> new file mode 100644
> index 0000000000000..171c6811a33e1
> --- /dev/null
> +++ b/drivers/gpio/gpio-amdptpci.c
[ ... ]
> +struct pt27_gpio {
> +struct device *dev;
> +struct gpio_generic_chipgen;
> + void __iomem *base;
> + struct pt27_saved_regs saved;
> + raw_spinlock_t lock;
> + u16 wake_mask;
> + bool use_msi;
> + int irq;
> +};
[Severity: Low]
There is a missing space between struct gpio_generic_chip and gen in
struct pt27_gpio, which causes compilation to fail when pt27_gpio_isr()
and pt27_gpio_probe() reference chip->gen.
> +
> +static void pt27_save_regs(struct pt27_gpio *chip)
> +{
> + chip->saved.dir = ioread32(chip->base + PT27_REG_DIR);
> + chip->saved.out = ioread32(chip->base + PT27_REG_OUT);
[ ... ]
> +static void pt27_restore_regs(struct pt27_gpio *chip)
> +{
> + iowrite32(chip->saved.dir, chip->base + PT27_REG_DIR);
> + iowrite32(chip->saved.out, chip->base + PT27_REG_OUT);
[Severity: Medium]
Can saving and restoring PT27_REG_DIR and PT27_REG_OUT under chip->lock
race with gpio-mmio operations and desynchronize the shadow registers?
In pt27_gpio_probe(), gpio_generic_chip_init() configures gpio-mmio to
manage PT27_REG_OUT and PT27_REG_DIR using chip->gen.lock as well as the
shadow state in chip->gen.sdata and chip->gen.sdir. However,
pt27_gpio_suspend(), pt27_gpio_runtime_suspend(), and pt27_gpio_resume()
call pt27_save_regs() and pt27_restore_regs() while holding only
chip->lock instead of chip->gen.lock (via gpio_generic_lock_irqsave()).
Because gc->can_sleep is false and the system sleep callbacks run during
the normal suspend/resume phase, concurrent atomic GPIO consumers calling
gpiod_set_value() or gpiod_direction_output() are not serialized against
pt27_save_regs() and pt27_restore_regs(), which can overwrite register
updates on resume and leave the hardware out of sync with chip->gen.sdata
and chip->gen.sdir.
[ ... ]
> +static int pt27_irq_reqres(struct irq_data *d)
> +{
> + struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
> + irq_hw_number_t hwirq = irqd_to_hwirq(d);
> + int ret;
> +
> + ret = gpiochip_reqres_irq(gc, hwirq);
> + return ret;
> +}
> +
> +static void pt27_irq_relres(struct irq_data *d)
> +{
> + struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
> + irq_hw_number_t hwirq = irqd_to_hwirq(d);
> +
> + gpiochip_relres_irq(gc, hwirq);
> +}
[Severity: High]
Should pt27_irq_reqres() and pt27_irq_relres() also manage runtime PM
references for the device?
Currently, pm_runtime_resume_and_get() and pm_runtime_put_autosuspend()
are only called in pt27_gpio_request() and pt27_gpio_free(). When a
consumer requests a GPIO interrupt directly through the irq_domain (for
example via request_irq() or ACPI GpioInt) without calling gpiod_request(),
gpiochip_reqres_irq() does not invoke gc->request().
If the controller has already runtime-suspended into PCI D3hot after the
2000 ms autosuspend delay, gpiochip_reqres_irq() -> gpiochip_lock_as_irq()
reads PT27_REG_DIR while in D3, gets 0xffffffff, treats the pin as an
output, and fails with -EIO. Furthermore, even if the device is in D0 when
the interrupt is requested, nothing holds a runtime PM reference, so the
device will autosuspend after 2000 ms and pt27_gpio_runtime_suspend() will
clear PT27_REG_INT_EN and mask all interrupts in PT27_REG_INT_MASK while
the IRQ is active.
[ ... ]
> +static int pt27_irq_set_wake(struct irq_data *d, unsigned int on)
> +{
> + struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
> + struct pt27_gpio *chip = gpiochip_get_data(gc);
> + irq_hw_number_t hwirq = irqd_to_hwirq(d);
> + unsigned int trig = irqd_get_trigger_type(d);
> + u16 wake_en, wake_lvl, wake_mode;
> +
> + if (hwirq >= PT27_WAKE_GPIOS) {
> + dev_dbg(chip->dev, "%s: GPIO%lu does not support wake (max GPIO%u)\n", __func__, hwirq, PT27_WAKE_GPIOS - 1);
> + return -EINVAL;
> + }
> +
> + if (on && trig == IRQ_TYPE_NONE)
> + return -EINVAL;
> +
> + guard(raw_spinlock_irqsave)(&chip->lock);
> +
> + wake_en = ioread16(chip->base + PT27_REG_WAKE_EN);
> + wake_lvl = ioread16(chip->base + PT27_REG_WAKE_LVL);
> + wake_mode = ioread16(chip->base + PT27_REG_WAKE_MODE);
[Severity: High]
What happens when pt27_irq_set_wake() is called while the controller is
runtime-suspended, or when the device later resumes?
When a consumer calls enable_irq_wake() during system suspend (or
disable_irq_wake() during system resume) while the GPIO controller is
runtime-suspended in PCI D3, pt27_irq_set_wake() performs ioread16() and
iowrite16() directly on PT27_REG_WAKE_EN, PT27_REG_WAKE_LVL, and
PT27_REG_WAKE_MODE without ensuring the PCI device is in D0 and without
updating chip->saved.wake_en, chip->saved.wake_lvl, and
chip->saved.wake_mode.
In addition to the MMIO accesses failing in D3, when pt27_gpio_resume() or
pt27_gpio_runtime_resume() subsequently calls pt27_restore_regs(), it
writes the stale chip->saved.wake_* values back to the hardware registers,
overwriting any wake configuration changes made after pt27_save_regs().
[ ... ]
> +static irqreturn_t pt27_gpio_isr(int irq, void *data)
> +{
> + struct pt27_gpio *chip = data;
> + unsigned long pending;
> + u32 stat, en, mask;
> +
> + scoped_guard(raw_spinlock_irqsave, &chip->lock) {
> +
> + stat = ioread32(chip->base + PT27_REG_INT_STAT);
> + en = ioread32(chip->base + PT27_REG_INT_EN);
> + mask = ioread32(chip->base + PT27_REG_INT_MASK);
> +
> + pending = stat & en & ~mask & PT27_GPIO_MASK;
> + }
[Severity: High]
When MSI/MSI-X is not used, pt27_gpio_probe() requests the interrupt with
IRQF_SHARED. If the PT27 controller is runtime-suspended in PCI D3 and
another device on the shared INTx line asserts an interrupt, won't
pt27_gpio_isr() read BAR0 MMIO registers while the device is powered down?
Should pt27_gpio_isr() check whether the device is runtime-active (for
example with pm_runtime_get_if_active()) before accessing hardware
registers?
[ ... ]
> +static int pt27_gpio_probe(struct pci_dev *pdev, const struct pci_device_id *id)
> +{
[ ... ]
> + pt27_hw_init(chip);
> +
> + ret = devm_add_action_or_reset(dev, pt27_hw_clear, chip);
> + if (ret)
> + return ret;
[ ... ]
> + pm_runtime_set_autosuspend_delay(dev, PT27_AUTOSUSPEND_DELAY_MS);
> + pm_runtime_use_autosuspend(dev);
> +
> + ret = devm_gpiochip_add_data(dev, gc, chip);
> + if (ret)
> + return dev_err_probe(dev, ret, "failed to register GPIO chip\n");
> +
> + ret = devm_request_irq(dev, chip->irq, pt27_gpio_isr,
> + irq_flags | IRQF_NO_THREAD, dev_name(dev), chip);
> + if (ret)
> + return dev_err_probe(dev, ret, "failed to request IRQ %d\n", chip->irq);
[Severity: Medium]
Is the devres registration order and runtime PM error cleanup in
pt27_gpio_probe() safe during probe and unbind?
First, devm_gpiochip_add_data() registers the GPIO chip and IRQ domain
before devm_request_irq() installs pt27_gpio_isr(). Consumers (including
acpi_gpiochip_request_interrupts() inside gpiochip_add_irqchip()) can
request and unmask GPIO interrupts before the parent IRQ handler is
registered. On driver unbind, reverse devres ordering frees chip->irq
before removing the gpiochip and before running pt27_hw_clear(), leaving a
window where hardware interrupts remain unmasked after the IRQ handler has
been removed.
Second, if devm_gpiochip_add_data() or devm_request_irq() fails after
pm_runtime_use_autosuspend(dev), pt27_gpio_probe() returns without calling
pm_runtime_dont_use_autosuspend(dev), leaving use_autosuspend enabled on
the PCI device.
[ ... ]
> +static int __maybe_unused pt27_gpio_suspend(struct device *dev)
> +{
> + struct pt27_gpio *chip = dev_get_drvdata(dev);
> +
> + if (pm_runtime_status_suspended(dev)) {
> + dev_dbg(dev, "%s: already runtime-suspended, skipping save\n", __func__);
> + return 0;
> + }
> +
> + scoped_guard(raw_spinlock_irqsave, &chip->lock) {
> + pt27_save_regs(chip);
> + iowrite32(0, chip->base + PT27_REG_INT_EN);
> + iowrite32(PT27_GPIO_MASK, chip->base + PT27_REG_INT_MASK);
> + iowrite32(PT27_GPIO_MASK, chip->base + PT27_REG_INT_STAT);
> + iowrite16(PT27_WAKE_MASK, chip->base + PT27_REG_WAKE_STAT);
> + }
> +
> + synchronize_irq(chip->irq);
> + return 0;
> +}
> +
> +static int __maybe_unused pt27_gpio_resume(struct device *dev)
> +{
> + struct pt27_gpio *chip = dev_get_drvdata(dev);
> + u16 wake_status;
> +
> + if (pm_runtime_status_suspended(dev))
> + return 0;
> +
> + scoped_guard(raw_spinlock_irqsave, &chip->lock) {
> + wake_status = ioread16(chip->base + PT27_REG_WAKE_STAT) & PT27_WAKE_MASK;
> + iowrite16(wake_status, chip->base + PT27_REG_WAKE_STAT);
> + pt27_restore_regs(chip);
> + }
> +
> + if (wake_status)
> + pm_wakeup_event(dev, 0);
> +
> + return 0;
> +}
[Severity: Medium]
When a wake-enabled GPIO interrupt fires while the controller is suspended,
how does the consumer's interrupt handler get invoked on resume?
In pt27_gpio_suspend() and pt27_gpio_runtime_suspend(), PT27_REG_INT_EN is
cleared to 0 and PT27_REG_INT_MASK is set to PT27_GPIO_MASK, so wake events
occurring during suspend are only latched in PT27_REG_WAKE_STAT. In
pt27_gpio_resume(), wake_status is read and cleared from
PT27_REG_WAKE_STAT, and pm_wakeup_event(dev, 0) is called, but
generic_handle_domain_irq() is never called for the set bits in
wake_status, so edge-triggered wakeup interrupts are dropped.
Also, in pt27_gpio_suspend(), if !device_may_wakeup(dev) (for example when
wakeup is disabled via sysfs), PT27_REG_WAKE_EN is left enabled in hardware
rather than being cleared before entering system sleep.
> +
> +static int __maybe_unused pt27_gpio_runtime_suspend(struct device *dev)
> +{
> + struct pt27_gpio *chip = dev_get_drvdata(dev);
> + int ret = 0;
> +
> + if (chip->wake_mask && !device_may_wakeup(dev)) {
> + ret = -EBUSY;
> + break;
> + }
> +
> + scoped_guard(raw_spinlock_irqsave, &chip->lock) {
> +
> + pt27_save_regs(chip);
> + iowrite32(0, chip->base + PT27_REG_INT_EN);
> + iowrite32(PT27_GPIO_MASK, chip->base + PT27_REG_INT_MASK);
> + iowrite32(PT27_GPIO_MASK, chip->base + PT27_REG_INT_STAT);
> + iowrite16(PT27_WAKE_MASK, chip->base + PT27_REG_WAKE_STAT);
> + }
[Severity: Medium]
In pt27_gpio_runtime_suspend(), the break statement is not inside a loop or
switch statement, which causes a build error when CONFIG_PM is enabled:
error: break statement not within loop or switch
Should this check be performed inside scoped_guard(raw_spinlock_irqsave,
&chip->lock) (since pt27_irq_set_wake() updates chip->wake_mask under
chip->lock) and return -EBUSY directly so it does not fall through and
disable PT27_REG_INT_EN when aborting runtime suspend?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261028084531.6237-1-Chloe_Chen@asmedia.com.tw?part=1
prev parent reply other threads:[~2026-10-28 9:06 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-28 8:45 [PATCH v3] gpio: amdptpci: add support for AMD PT PCI controller Szuying Chen
2026-10-28 9:06 ` sashiko-bot [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=sashiko-outbox-187649@kernel.org \
--to=sashiko-bot@kernel.org \
--cc=Chloe_Chen@asmedia.com.tw \
--cc=linux-gpio@vger.kernel.org \
--cc=sashiko-reviews@lists.linux.dev \
/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