From a136d2c30b850f94ee7b39f842eaede8c0a1c490 Mon Sep 17 00:00:00 2001 From: Jae Hyun Yoo 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 --- .../arm/boot/dts/aspeed-bmc-intel-ast2600.dts | 8 +++ drivers/tty/serial/8250/8250_early.c | 1 + drivers/tty/serial/8250/8250_of.c | 63 +++++++++++++++++++ 3 files changed, 72 insertions(+) diff --git a/arch/arm/boot/dts/aspeed-bmc-intel-ast2600.dts b/arch/arm/boot/dts/aspeed-bmc-intel-ast2600.dts index 3218884b90f4..08e1f060341e 100644 --- a/arch/arm/boot/dts/aspeed-bmc-intel-ast2600.dts +++ b/arch/arm/boot/dts/aspeed-bmc-intel-ast2600.dts @@ -375,6 +375,8 @@ }; &uart1 { + compatible = "aspeed,ast2600-uart"; + reg = <0x1e783000 0x20>, <0x1e6e2014 0x4>, <0x1e78307c 0x4>; status = "okay"; pinctrl-0 = <&pinctrl_txd1_default &pinctrl_rxd1_default @@ -387,6 +389,8 @@ }; &uart2 { + compatible = "aspeed,ast2600-uart"; + reg = <0x1e78d000 0x20>, <0x1e6e2014 0x4>, <0x1e78d07c 0x4>; status = "okay"; pinctrl-0 = <&pinctrl_txd2_default &pinctrl_rxd2_default @@ -399,11 +403,15 @@ }; &uart3 { + compatible = "aspeed,ast2600-uart"; + reg = <0x1e78e000 0x20>, <0x1e6e2014 0x4>, <0x1e78e07c 0x4>; status = "okay"; pinctrl-0 = <>; }; &uart4 { + compatible = "aspeed,ast2600-uart"; + reg = <0x1e78f000 0x20>, <0x1e6e2014 0x4>, <0x1e78f07c 0x4>; status = "okay"; pinctrl-0 = <>; }; diff --git a/drivers/tty/serial/8250/8250_early.c b/drivers/tty/serial/8250/8250_early.c index 70d7826788f5..56c4725e1b04 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 65e9045dafe6..4d94c9f6a422 100644 --- a/drivers/tty/serial/8250/8250_of.c +++ b/drivers/tty/serial/8250/8250_of.c @@ -15,6 +15,7 @@ #include #include #include +#include #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 */ @@ -229,6 +245,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); @@ -250,6 +307,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); @@ -319,6 +381,7 @@ static const struct of_device_id of_platform_serial_table[] = { .data = (void *)PORT_XSCALE, }, { .compatible = "ti,da830-uart", .data = (void *)PORT_DA830, }, { .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