summaryrefslogtreecommitdiff
path: root/meta-openbmc-mods/meta-ast2600/recipes-kernel/linux/linux-aspeed/0004-Add-a-workaround-to-cover-UART-interrupt-bug-in-AST2.patch
diff options
context:
space:
mode:
Diffstat (limited to 'meta-openbmc-mods/meta-ast2600/recipes-kernel/linux/linux-aspeed/0004-Add-a-workaround-to-cover-UART-interrupt-bug-in-AST2.patch')
-rw-r--r--meta-openbmc-mods/meta-ast2600/recipes-kernel/linux/linux-aspeed/0004-Add-a-workaround-to-cover-UART-interrupt-bug-in-AST2.patch269
1 files changed, 269 insertions, 0 deletions
diff --git a/meta-openbmc-mods/meta-ast2600/recipes-kernel/linux/linux-aspeed/0004-Add-a-workaround-to-cover-UART-interrupt-bug-in-AST2.patch b/meta-openbmc-mods/meta-ast2600/recipes-kernel/linux/linux-aspeed/0004-Add-a-workaround-to-cover-UART-interrupt-bug-in-AST2.patch
new file mode 100644
index 000000000..757b00a25
--- /dev/null
+++ b/meta-openbmc-mods/meta-ast2600/recipes-kernel/linux/linux-aspeed/0004-Add-a-workaround-to-cover-UART-interrupt-bug-in-AST2.patch
@@ -0,0 +1,269 @@
+From a9b43dbf68a4b5650dd98332243d4a2951717eb5 Mon Sep 17 00:00:00 2001
+From: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
+Date: Mon, 27 Apr 2020 12:11:06 -0700
+Subject: [PATCH] Add a workaround to cover UART interrupt bug in AST2600 A0
+
+This commit adds a workaround to cover UART interrupt bug in
+AST2600 A0 revision. It makes infinite reading on the UART +0x7c
+register for clearing abnormal interrupts in every milli-second.
+
+Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
+---
+ .../arm/boot/dts/aspeed-bmc-intel-ast2600.dts | 4 ++
+ drivers/tty/serial/8250/8250_aspeed.c | 56 ++++++++++++++++-
+ drivers/tty/serial/8250/8250_early.c | 1 +
+ drivers/tty/serial/8250/8250_of.c | 63 +++++++++++++++++++
+ 4 files changed, 123 insertions(+), 1 deletion(-)
+
+diff --git a/arch/arm/boot/dts/aspeed-bmc-intel-ast2600.dts b/arch/arm/boot/dts/aspeed-bmc-intel-ast2600.dts
+index c648c123c315..bf375634082c 100644
+--- a/arch/arm/boot/dts/aspeed-bmc-intel-ast2600.dts
++++ b/arch/arm/boot/dts/aspeed-bmc-intel-ast2600.dts
+@@ -392,6 +392,7 @@
+ };
+
+ &uart1 {
++ reg = <0x1e783000 0x20>, <0x1e6e2014 0x4>, <0x1e78307c 0x4>;
+ status = "okay";
+ pinctrl-0 = <&pinctrl_txd1_default
+ &pinctrl_rxd1_default
+@@ -404,6 +405,7 @@
+ };
+
+ &uart2 {
++ reg = <0x1e78d000 0x20>, <0x1e6e2014 0x4>, <0x1e78d07c 0x4>;
+ status = "okay";
+ pinctrl-0 = <&pinctrl_txd2_default
+ &pinctrl_rxd2_default
+@@ -416,11 +418,13 @@
+ };
+
+ &uart3 {
++ reg = <0x1e78e000 0x20>, <0x1e6e2014 0x4>, <0x1e78e07c 0x4>;
+ status = "okay";
+ pinctrl-0 = <>;
+ };
+
+ &uart4 {
++ reg = <0x1e78f000 0x20>, <0x1e6e2014 0x4>, <0x1e78f07c 0x4>;
+ status = "okay";
+ pinctrl-0 = <>;
+ };
+diff --git a/drivers/tty/serial/8250/8250_aspeed.c b/drivers/tty/serial/8250/8250_aspeed.c
+index bb3955bb8c24..1ba4423d5e2f 100644
+--- a/drivers/tty/serial/8250/8250_aspeed.c
++++ b/drivers/tty/serial/8250/8250_aspeed.c
+@@ -74,6 +74,10 @@ struct ast8250_data {
+
+ struct ast8250_vuart vuart;
+ struct ast8250_udma dma;
++
++ struct workqueue_struct *work_queue;
++ struct delayed_work work_handler;
++ void __iomem *wa_base;
+ };
+
+ static void ast8250_dma_tx_complete(int tx_rb_rptr, void *id)
+@@ -336,12 +340,25 @@ static int __maybe_unused ast8250_resume(struct device *dev)
+ return 0;
+ }
+
++#define WA_DELAY_JIFFIES msecs_to_jiffies(1)
++static void ast8250_clear_abnormal_int_flags(struct work_struct *work)
++{
++ struct delayed_work *dwork = to_delayed_work(work);
++ struct ast8250_data *data = container_of(dwork, struct ast8250_data,
++ work_handler);
++
++ (void) readl(data->wa_base);
++ queue_delayed_work(data->work_queue, &data->work_handler,
++ WA_DELAY_JIFFIES);
++}
++
+ static int ast8250_probe(struct platform_device *pdev)
+ {
+ int rc;
+ struct uart_8250_port uart = {};
+ struct uart_port *port = &uart.port;
+ struct device *dev = &pdev->dev;
++ void __iomem *chip_id_base;
+ struct ast8250_data *data;
+
+ struct resource *res;
+@@ -454,6 +471,37 @@ static int ast8250_probe(struct platform_device *pdev)
+ pm_runtime_enable(&pdev->dev);
+
+ platform_set_drvdata(pdev, data);
++
++ #define REV_ID_AST2600A0 0x05000303
++ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
++ if (!res || resource_size(res) < 2)
++ return 0;
++
++ data->wa_base = devm_platform_ioremap_resource(pdev, 2);
++ if (IS_ERR(data->wa_base))
++ return 0;
++
++ chip_id_base = devm_ioremap_resource(dev, res);
++ if (IS_ERR(chip_id_base))
++ return 0;
++
++ if (readl(chip_id_base) == REV_ID_AST2600A0) {
++ data->work_queue = alloc_ordered_workqueue(pdev->name, 0);
++ if (data->work_queue) {
++ INIT_DELAYED_WORK(&data->work_handler,
++ ast8250_clear_abnormal_int_flags);
++ queue_delayed_work(data->work_queue,
++ &data->work_handler,
++ WA_DELAY_JIFFIES);
++ dev_info(dev, "AST2600 A0 WA initiated\n");
++ } else {
++ dev_err(dev, "Can't enable AST2600 A0 UART WA\n");
++ }
++ }
++
++ devm_iounmap(dev, chip_id_base);
++ devm_release_mem_region(dev, res->start, resource_size(res));
++
+ return 0;
+ }
+
+@@ -464,7 +512,13 @@ static int ast8250_remove(struct platform_device *pdev)
+ if (data->is_vuart)
+ ast8250_vuart_set_enable(data, false);
+
+- serial8250_unregister_port(data->line);
++ if (data->work_queue) {
++ cancel_delayed_work_sync(&data->work_handler);
++ destroy_workqueue(data->work_queue);
++ }
++
++ serial8250_unregister_port(data->line);
++
+ return 0;
+ }
+
+diff --git a/drivers/tty/serial/8250/8250_early.c b/drivers/tty/serial/8250/8250_early.c
+index c171ce6db691..6d7bb63fe0c6 100644
+--- a/drivers/tty/serial/8250/8250_early.c
++++ b/drivers/tty/serial/8250/8250_early.c
+@@ -180,6 +180,7 @@ OF_EARLYCON_DECLARE(ns16550, "ns16550", early_serial8250_setup);
+ OF_EARLYCON_DECLARE(ns16550a, "ns16550a", early_serial8250_setup);
+ OF_EARLYCON_DECLARE(uart, "nvidia,tegra20-uart", early_serial8250_setup);
+ OF_EARLYCON_DECLARE(uart, "snps,dw-apb-uart", early_serial8250_setup);
++OF_EARLYCON_DECLARE(uart, "aspeed,ast2600-uart", early_serial8250_setup);
+
+ #ifdef CONFIG_SERIAL_8250_OMAP
+
+diff --git a/drivers/tty/serial/8250/8250_of.c b/drivers/tty/serial/8250/8250_of.c
+index bce28729dd7b..6159d8af6fef 100644
+--- a/drivers/tty/serial/8250/8250_of.c
++++ b/drivers/tty/serial/8250/8250_of.c
+@@ -15,6 +15,7 @@
+ #include <linux/pm_runtime.h>
+ #include <linux/clk.h>
+ #include <linux/reset.h>
++#include <linux/workqueue.h>
+
+ #include "8250.h"
+
+@@ -23,6 +24,9 @@ struct of_serial_info {
+ struct reset_control *rst;
+ int type;
+ int line;
++ struct workqueue_struct *work_queue;
++ struct delayed_work work_handler;
++ void __iomem *wa_base;
+ };
+
+ /*
+@@ -181,6 +185,18 @@ static int of_platform_serial_setup(struct platform_device *ofdev,
+ return ret;
+ }
+
++#define WA_DELAY_JIFFIES msecs_to_jiffies(1)
++static void clear_abnormal_int_flags(struct work_struct *work)
++{
++ struct delayed_work *dwork = to_delayed_work(work);
++ struct of_serial_info *info = container_of(dwork, struct of_serial_info,
++ work_handler);
++
++ (void) readl(info->wa_base);
++ queue_delayed_work(info->work_queue, &info->work_handler,
++ WA_DELAY_JIFFIES);
++}
++
+ /*
+ * Try to register a serial port
+ */
+@@ -233,6 +249,47 @@ static int of_platform_serial_probe(struct platform_device *ofdev)
+ if (ret < 0)
+ goto err_dispose;
+
++ if (of_device_is_compatible(ofdev->dev.of_node,
++ "aspeed,ast2600-uart")) {
++ #define REV_ID_AST2600A0 0x05000303
++ void __iomem *chip_id_base;
++ struct resource *res = platform_get_resource(ofdev,
++ IORESOURCE_MEM, 1);
++
++ if (!res || resource_size(res) < 2)
++ goto skip_wa;
++
++ info->wa_base = devm_platform_ioremap_resource(ofdev, 2);
++ if (IS_ERR(info->wa_base))
++ goto skip_wa;
++
++ chip_id_base = devm_ioremap_resource(&ofdev->dev, res);
++ if (IS_ERR(chip_id_base))
++ goto skip_wa;
++
++ if (readl(chip_id_base) == REV_ID_AST2600A0) {
++ info->work_queue = alloc_ordered_workqueue(ofdev->name,
++ 0);
++ if (info->work_queue) {
++ INIT_DELAYED_WORK(&info->work_handler,
++ clear_abnormal_int_flags);
++ queue_delayed_work(info->work_queue,
++ &info->work_handler,
++ WA_DELAY_JIFFIES);
++ dev_info(&ofdev->dev,
++ "AST2600 A0 WA initiated\n");
++ } else {
++ dev_err(&ofdev->dev,
++ "Can't enable AST2600 A0 UART WA\n");
++ }
++ }
++
++ devm_iounmap(&ofdev->dev, chip_id_base);
++ devm_release_mem_region(&ofdev->dev, res->start,
++ resource_size(res));
++ }
++
++skip_wa:
+ info->type = port_type;
+ info->line = ret;
+ platform_set_drvdata(ofdev, info);
+@@ -254,6 +311,11 @@ static int of_platform_serial_remove(struct platform_device *ofdev)
+ {
+ struct of_serial_info *info = platform_get_drvdata(ofdev);
+
++ if (info->work_queue) {
++ cancel_delayed_work_sync(&info->work_handler);
++ destroy_workqueue(info->work_queue);
++ }
++
+ serial8250_unregister_port(info->line);
+
+ reset_control_assert(info->rst);
+@@ -324,6 +386,7 @@ static const struct of_device_id of_platform_serial_table[] = {
+ { .compatible = "ti,da830-uart", .data = (void *)PORT_DA830, },
+ { .compatible = "nuvoton,wpcm450-uart", .data = (void *)PORT_NPCM, },
+ { .compatible = "nuvoton,npcm750-uart", .data = (void *)PORT_NPCM, },
++ { .compatible = "aspeed,ast2600-uart", .data = (void *)PORT_16550A, },
+ { /* end of list */ },
+ };
+ MODULE_DEVICE_TABLE(of, of_platform_serial_table);
+--
+2.17.1
+