126 lines
4.3 KiB
Diff
Executable File
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
|
|
|