Files
OpenBMC/meta-luxshare/meta-bhs/recipes-kernel/linux/linux-aspeed/0001-Workaround-VW-interrupt-design-issue.patch
2026-04-23 17:07:55 +08:00

126 lines
4.3 KiB
Diff
Executable File

From 91773622d78c8a32c5591a8d1e7f9f4cb51486d8 Mon Sep 17 00:00:00 2001
From: Sujoy Ray <sujoy.ray@intel.com>
Date: Wed, 20 Mar 2024 14:22:49 -0700
Subject: [PATCH] Workaround VW interrupt design issue
In AST2600, eSPI reset interrupt is triggered at both edges.
In the ISR GPIO direction register is set, and it has been found
that if it is set after the first interrupt, it gets cleared when
the 2nd edge arrives. To fix the issue workqueue is created at it
is delayed by 500ms. The GPIO direction register is set in the
context of delayed workqueue.
Signed-off-by: Sujoy Ray <sujoy.ray@intel.com>
---
drivers/gpio/gpio-aspeed-espi-vw.c | 52 ++++++++++++++++++++++++------
1 file changed, 43 insertions(+), 9 deletions(-)
diff --git a/drivers/gpio/gpio-aspeed-espi-vw.c b/drivers/gpio/gpio-aspeed-espi-vw.c
index 0c78d78fa540..418dc7266662 100644
--- a/drivers/gpio/gpio-aspeed-espi-vw.c
+++ b/drivers/gpio/gpio-aspeed-espi-vw.c
@@ -6,6 +6,8 @@
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
+#include <linux/workqueue.h>
+#include <linux/timer.h>
#include <linux/mfd/syscon.h>
#include <linux/soc/aspeed/aspeed-espi.h>
@@ -49,6 +51,15 @@ struct aspeed_espi_gpio {
spinlock_t lock;
};
+struct aspeed_espi_vw_delayed_work_struct {
+ struct delayed_work delayed_work;
+ void *arg;
+};
+
+static struct workqueue_struct *aspeed_espi_gpio_workqueue;
+static void aspeed_espi_vw_gpio_workqueue(struct work_struct *work);
+static struct aspeed_espi_vw_delayed_work_struct aspeed_vw_work;
+
static void aspeed_espi_vw_gpio_enable(struct regmap *map, u32 dir_mask)
{
regmap_update_bits(map, ASPEED_ESPI_INT_EN, ASPEED_ESPI_INT_EN_VW_MASK,
@@ -75,6 +86,26 @@ static void set_nth_bit(u32 *n, uint8_t offset, u32 val)
*n = *n | (1ul << offset);
}
+void aspeed_espi_vw_gpio_workqueue(struct work_struct *work)
+{
+ struct aspeed_espi_gpio *gpio;
+ struct aspeed_espi_vw_delayed_work_struct *work_ptr;
+ unsigned long flags;
+
+ struct delayed_work *dw = container_of(work, struct delayed_work, work);
+
+ work_ptr = container_of(dw, struct aspeed_espi_vw_delayed_work_struct,
+ delayed_work);
+ gpio = work_ptr->arg;
+ aspeed_espi_vw_gpio_enable(gpio->map, gpio->dir_mask);
+ dev_dbg(gpio->dev, "Resetting VGPIO value [%08X] from workqueue\n",
+ cached_reg_val);
+ spin_lock_irqsave(&gpio->lock, flags);
+ regmap_update_bits(gpio->map, ASPEED_ESPI_VW_GPIO_VAL, gpio->dir_mask,
+ cached_reg_val);
+ spin_unlock_irqrestore(&gpio->lock, flags);
+}
+
static int vgpio_get_value(struct gpio_chip *gc, unsigned int offset)
{
struct aspeed_espi_gpio *gpio = gpiochip_get_data(gc);
@@ -213,22 +244,16 @@ static int aspeed_espi_vw_gpio_init(struct device *dev, struct aspeed_espi_gpio
static void aspeed_espi_vw_irq(int irq, void *arg)
{
struct aspeed_espi_gpio *gpio = arg;
- unsigned long flags;
u32 sts;
-
if (regmap_read(gpio->map, ASPEED_ESPI_INT_STS, &sts)) {
dev_dbg(gpio->dev, "Error reading int status\n");
return;
}
if (sts & ASPEED_ESPI_INT_STS_HW_RESET) {
- dev_dbg(gpio->dev, "Resetting VGPIO value [%08X]\n", cached_reg_val);
- aspeed_espi_vw_gpio_enable(gpio->map, gpio->dir_mask);
-
- spin_lock_irqsave(&gpio->lock, flags);
- regmap_update_bits(gpio->map, ASPEED_ESPI_VW_GPIO_VAL, gpio->dir_mask,
- cached_reg_val);
- spin_unlock_irqrestore(&gpio->lock, flags);
+ dev_dbg(gpio->dev, "Scheduling workqueue for deferred processing\n");
+ queue_delayed_work(aspeed_espi_gpio_workqueue, &aspeed_vw_work.delayed_work,
+ msecs_to_jiffies(500));
}
/* Clearing of status register will be done from parent driver*/
}
@@ -257,6 +282,14 @@ static int aspeed_espi_gpio_probe(struct platform_device *pdev)
aspeed_espi_register_gpio(pdev->dev.parent, aspeed_espi_vw_irq, gpio);
+ aspeed_espi_gpio_workqueue = create_workqueue("aspeed_espi_workqueue");
+ if (!aspeed_espi_gpio_workqueue) {
+ pr_err("Failed to create workqueue");
+ return -ENOMEM;
+ }
+
+ aspeed_vw_work.arg = gpio;
+ INIT_DELAYED_WORK(&aspeed_vw_work.delayed_work, aspeed_espi_vw_gpio_workqueue);
return ret;
}
@@ -265,6 +298,7 @@ static int aspeed_espi_gpio_remove(struct platform_device *pdev)
struct aspeed_espi_gpio *gpio = dev_get_drvdata(&pdev->dev);
aspeed_espi_vw_gpio_disable(gpio->map);
+ cancel_delayed_work_sync(&aspeed_vw_work.delayed_work);
return 0;
}
--
2.25.1