From 1fc0d70f658da30091bcd49f9bf29aecd6b99ba7 Mon Sep 17 00:00:00 2001 From: "Jason M. Bills" Date: Thu, 6 Jan 2022 13:50:19 -0800 Subject: Update to internal 0.86 Signed-off-by: Jason M. Bills --- ...round-to-cover-UART-interrupt-bug-in-AST2.patch | 178 --- ...-Add-Aspeed-UART-driver-with-DMA-supporte.patch | 1184 ++++++++++++++++++++ ...-serial-8250-Fix-RX-DMA-time-out-property.patch | 29 + ...3-serial-8250_aspeed-Make-port-type-fixed.patch | 57 + ...round-to-cover-UART-interrupt-bug-in-AST2.patch | 269 +++++ .../linux/linux-aspeed/intel-ast2600.cfg | 4 +- .../recipes-kernel/linux/linux-aspeed_%.bbappend | 5 +- 7 files changed, 1546 insertions(+), 180 deletions(-) delete mode 100644 meta-openbmc-mods/meta-ast2600/recipes-kernel/linux/linux-aspeed/0001-Add-a-workaround-to-cover-UART-interrupt-bug-in-AST2.patch create mode 100644 meta-openbmc-mods/meta-ast2600/recipes-kernel/linux/linux-aspeed/0001-serial-8250-Add-Aspeed-UART-driver-with-DMA-supporte.patch create mode 100644 meta-openbmc-mods/meta-ast2600/recipes-kernel/linux/linux-aspeed/0002-serial-8250-Fix-RX-DMA-time-out-property.patch create mode 100644 meta-openbmc-mods/meta-ast2600/recipes-kernel/linux/linux-aspeed/0003-serial-8250_aspeed-Make-port-type-fixed.patch create mode 100644 meta-openbmc-mods/meta-ast2600/recipes-kernel/linux/linux-aspeed/0004-Add-a-workaround-to-cover-UART-interrupt-bug-in-AST2.patch (limited to 'meta-openbmc-mods/meta-ast2600/recipes-kernel/linux') diff --git a/meta-openbmc-mods/meta-ast2600/recipes-kernel/linux/linux-aspeed/0001-Add-a-workaround-to-cover-UART-interrupt-bug-in-AST2.patch b/meta-openbmc-mods/meta-ast2600/recipes-kernel/linux/linux-aspeed/0001-Add-a-workaround-to-cover-UART-interrupt-bug-in-AST2.patch deleted file mode 100644 index 05e40afb3..000000000 --- a/meta-openbmc-mods/meta-ast2600/recipes-kernel/linux/linux-aspeed/0001-Add-a-workaround-to-cover-UART-interrupt-bug-in-AST2.patch +++ /dev/null @@ -1,178 +0,0 @@ -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 - diff --git a/meta-openbmc-mods/meta-ast2600/recipes-kernel/linux/linux-aspeed/0001-serial-8250-Add-Aspeed-UART-driver-with-DMA-supporte.patch b/meta-openbmc-mods/meta-ast2600/recipes-kernel/linux/linux-aspeed/0001-serial-8250-Add-Aspeed-UART-driver-with-DMA-supporte.patch new file mode 100644 index 000000000..525b910d0 --- /dev/null +++ b/meta-openbmc-mods/meta-ast2600/recipes-kernel/linux/linux-aspeed/0001-serial-8250-Add-Aspeed-UART-driver-with-DMA-supporte.patch @@ -0,0 +1,1184 @@ +From a4897aed25195742ec9fd992a52a6e7bf872f318 Mon Sep 17 00:00:00 2001 +From: "Chia-Wei, Wang" +Date: Wed, 22 Jul 2020 13:46:21 +0800 +Subject: [PATCH] serial: 8250: Add Aspeed UART driver with DMA supported + +This patch adds drivers for Aspeed UARTs, which are 16550A compatible. +The drivers includes an wrapper to support the extended DMA feature of +UART devices and another UDMA driver to control the UART DMA engine. + +Signed-off-by: Chia-Wei, Wang +--- + arch/arm/boot/dts/aspeed-ast2600-evb.dts | 5 - + .../arm/boot/dts/aspeed-bmc-intel-ast2600.dts | 2 - + arch/arm/boot/dts/aspeed-g6.dtsi | 28 +- + drivers/soc/aspeed/Kconfig | 8 + + drivers/soc/aspeed/Makefile | 1 + + drivers/soc/aspeed/aspeed-udma.c | 441 ++++++++++++++++ + drivers/tty/serial/8250/8250_aspeed.c | 494 ++++++++++++++++++ + drivers/tty/serial/8250/Kconfig | 8 + + drivers/tty/serial/8250/Makefile | 1 + + include/linux/soc/aspeed/aspeed-udma.h | 30 ++ + 10 files changed, 997 insertions(+), 21 deletions(-) + create mode 100644 drivers/soc/aspeed/aspeed-udma.c + create mode 100644 drivers/tty/serial/8250/8250_aspeed.c + create mode 100644 include/linux/soc/aspeed/aspeed-udma.h + +diff --git a/arch/arm/boot/dts/aspeed-ast2600-evb.dts b/arch/arm/boot/dts/aspeed-ast2600-evb.dts +index acbd1c947465..913749205c1d 100644 +--- a/arch/arm/boot/dts/aspeed-ast2600-evb.dts ++++ b/arch/arm/boot/dts/aspeed-ast2600-evb.dts +@@ -180,11 +180,6 @@ + }; + }; + +-&uart5 { +- // Workaround for A0 +- compatible = "snps,dw-apb-uart"; +-}; +- + &i2c0 { + status = "okay"; + +diff --git a/arch/arm/boot/dts/aspeed-bmc-intel-ast2600.dts b/arch/arm/boot/dts/aspeed-bmc-intel-ast2600.dts +index 02c837c3e2c4..210d2bbdf836 100644 +--- a/arch/arm/boot/dts/aspeed-bmc-intel-ast2600.dts ++++ b/arch/arm/boot/dts/aspeed-bmc-intel-ast2600.dts +@@ -427,8 +427,6 @@ + + &uart5 { + status = "okay"; +- // Workaround for A0 +- compatible = "snps,dw-apb-uart"; + }; + + &uart_routing { +diff --git a/arch/arm/boot/dts/aspeed-g6.dtsi b/arch/arm/boot/dts/aspeed-g6.dtsi +index e76dfb73430e..001ecf9ad33c 100644 +--- a/arch/arm/boot/dts/aspeed-g6.dtsi ++++ b/arch/arm/boot/dts/aspeed-g6.dtsi +@@ -524,23 +524,22 @@ + }; + + uart1: serial@1e783000 { +- compatible = "ns16550a"; ++ compatible = "aspeed,ast2600-uart"; + reg = <0x1e783000 0x20>; +- reg-shift = <2>; +- reg-io-width = <4>; + interrupts = ; + clocks = <&syscon ASPEED_CLK_GATE_UART1CLK>; + resets = <&lpc_reset 4>; + no-loopback-test; ++ dma-mode; ++ dma-channel = <0>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_txd1_default &pinctrl_rxd1_default>; + status = "disabled"; + }; + + uart5: serial@1e784000 { +- compatible = "ns16550a"; ++ compatible = "aspeed,ast2600-uart"; + reg = <0x1e784000 0x1000>; +- reg-shift = <2>; + interrupts = ; + clocks = <&syscon ASPEED_CLK_GATE_UART5CLK>; + no-loopback-test; +@@ -754,10 +753,8 @@ + }; + + uart2: serial@1e78d000 { +- compatible = "ns16550a"; ++ compatible = "aspeed,ast2600-uart"; + reg = <0x1e78d000 0x20>; +- reg-shift = <2>; +- reg-io-width = <4>; + interrupts = ; + clocks = <&syscon ASPEED_CLK_GATE_UART2CLK>; + resets = <&lpc_reset 5>; +@@ -768,10 +765,8 @@ + }; + + uart3: serial@1e78e000 { +- compatible = "ns16550a"; ++ compatible = "aspeed,ast2600-uart"; + reg = <0x1e78e000 0x20>; +- reg-shift = <2>; +- reg-io-width = <4>; + interrupts = ; + clocks = <&syscon ASPEED_CLK_GATE_UART3CLK>; + resets = <&lpc_reset 6>; +@@ -782,10 +777,8 @@ + }; + + uart4: serial@1e78f000 { +- compatible = "ns16550a"; ++ compatible = "aspeed,ast2600-uart"; + reg = <0x1e78f000 0x20>; +- reg-shift = <2>; +- reg-io-width = <4>; + interrupts = ; + clocks = <&syscon ASPEED_CLK_GATE_UART4CLK>; + resets = <&lpc_reset 7>; +@@ -834,6 +827,13 @@ + clocks = <&syscon ASPEED_CLK_GATE_FSICLK>; + status = "disabled"; + }; ++ ++ udma: uart-dma@1e79e000 { ++ compatible = "aspeed,ast2600-udma"; ++ reg = <0x1e79e000 0x400>; ++ interrupts = ; ++ }; ++ + }; + }; + }; +diff --git a/drivers/soc/aspeed/Kconfig b/drivers/soc/aspeed/Kconfig +index d36ee43451a5..ca284ac2f0ab 100644 +--- a/drivers/soc/aspeed/Kconfig ++++ b/drivers/soc/aspeed/Kconfig +@@ -90,6 +90,14 @@ config ASPEED_XDMA + SoCs. The XDMA engine can perform PCIe DMA operations between the BMC + and a host processor. + ++config ASPEED_UDMA ++ tristate "Aspeed UDMA Engine Driver" ++ depends on SOC_ASPEED && REGMAP && MFD_SYSCON && HAS_DMA ++ help ++ Enable support for the Aspeed UDMA Engine found on the Aspeed AST2XXX ++ SOCs. The UDMA engine can perform UART DMA operations between the memory ++ buffer and the UART/VUART devices. ++ + config ASPEED_VGA_SHAREDMEM + tristate "Aspeed VGA Shared memory" + help +diff --git a/drivers/soc/aspeed/Makefile b/drivers/soc/aspeed/Makefile +index f3afc32b58b9..e6248ecdeee3 100644 +--- a/drivers/soc/aspeed/Makefile ++++ b/drivers/soc/aspeed/Makefile +@@ -8,5 +8,6 @@ obj-$(CONFIG_ASPEED_LPC_SNOOP) += aspeed-lpc-snoop.o + obj-$(CONFIG_ASPEED_P2A_CTRL) += aspeed-p2a-ctrl.o + obj-$(CONFIG_ASPEED_SOCINFO) += aspeed-socinfo.o + obj-$(CONFIG_ASPEED_XDMA) += aspeed-xdma.o ++obj-$(CONFIG_ASPEED_UDMA) += aspeed-udma.o + obj-$(CONFIG_ASPEED_VGA_SHAREDMEM) += aspeed-vga-sharedmem.o + obj-$(CONFIG_ASPEED_MCTP) += aspeed-mctp.o +diff --git a/drivers/soc/aspeed/aspeed-udma.c b/drivers/soc/aspeed/aspeed-udma.c +new file mode 100644 +index 000000000000..01b73ebe1880 +--- /dev/null ++++ b/drivers/soc/aspeed/aspeed-udma.c +@@ -0,0 +1,441 @@ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define DEVICE_NAME "aspeed-udma" ++ ++/* UART DMA registers offset */ ++#define UDMA_TX_DMA_EN 0x000 ++#define UDMA_RX_DMA_EN 0x004 ++#define UDMA_TIMEOUT_TIMER 0x00c ++#define UDMA_TX_DMA_RST 0x020 ++#define UDMA_RX_DMA_RST 0x024 ++#define UDMA_TX_DMA_INT_EN 0x030 ++#define UDMA_TX_DMA_INT_STAT 0x034 ++#define UDMA_RX_DMA_INT_EN 0x038 ++#define UDMA_RX_DMA_INT_STAT 0x03c ++ ++#define UDMA_CHX_OFF(x) ((x) * 0x20) ++#define UDMA_CHX_TX_RD_PTR(x) (0x040 + UDMA_CHX_OFF(x)) ++#define UDMA_CHX_TX_WR_PTR(x) (0x044 + UDMA_CHX_OFF(x)) ++#define UDMA_CHX_TX_BUF_BASE(x) (0x048 + UDMA_CHX_OFF(x)) ++#define UDMA_CHX_TX_CTRL(x) (0x04c + UDMA_CHX_OFF(x)) ++#define UDMA_TX_CTRL_TMOUT_DISABLE BIT(4) ++#define UDMA_TX_CTRL_BUFSZ_MASK GENMASK(3, 0) ++#define UDMA_TX_CTRL_BUFSZ_SHIFT 0 ++#define UDMA_CHX_RX_RD_PTR(x) (0x050 + UDMA_CHX_OFF(x)) ++#define UDMA_CHX_RX_WR_PTR(x) (0x054 + UDMA_CHX_OFF(x)) ++#define UDMA_CHX_RX_BUF_BASE(x) (0x058 + UDMA_CHX_OFF(x)) ++#define UDMA_CHX_RX_CTRL(x) (0x05c + UDMA_CHX_OFF(x)) ++#define UDMA_RX_CTRL_TMOUT_DISABLE BIT(4) ++#define UDMA_RX_CTRL_BUFSZ_MASK GENMASK(3, 0) ++#define UDMA_RX_CTRL_BUFSZ_SHIFT 0 ++ ++#define UDMA_MAX_CHANNEL 14 ++#define UDMA_TIMEOUT 0x200 ++ ++enum aspeed_udma_bufsz_code { ++ UDMA_BUFSZ_CODE_1KB, ++ UDMA_BUFSZ_CODE_4KB, ++ UDMA_BUFSZ_CODE_16KB, ++ UDMA_BUFSZ_CODE_64KB, ++ ++ /* ++ * 128KB and above are supported ONLY for ++ * virtual UARTs. For physical UARTs, the ++ * size code is wrapped around at the 64K ++ * boundary. ++ */ ++ UDMA_BUFSZ_CODE_128KB, ++ UDMA_BUFSZ_CODE_256KB, ++ UDMA_BUFSZ_CODE_512KB, ++ UDMA_BUFSZ_CODE_1024KB, ++ UDMA_BUFSZ_CODE_2048KB, ++ UDMA_BUFSZ_CODE_4096KB, ++ UDMA_BUFSZ_CODE_8192KB, ++ UDMA_BUFSZ_CODE_16384KB, ++}; ++ ++struct aspeed_udma_chan { ++ dma_addr_t dma_addr; ++ ++ struct circ_buf *rb; ++ u32 rb_sz; ++ ++ aspeed_udma_cb_t cb; ++ void *cb_arg; ++ ++ bool dis_tmout; ++}; ++ ++struct aspeed_udma { ++ struct device *dev; ++ u8 __iomem *regs; ++ u32 irq; ++ struct aspeed_udma_chan tx_chs[UDMA_MAX_CHANNEL]; ++ struct aspeed_udma_chan rx_chs[UDMA_MAX_CHANNEL]; ++ spinlock_t lock; ++}; ++ ++struct aspeed_udma udma[1]; ++ ++static int aspeed_udma_get_bufsz_code(u32 buf_sz) ++{ ++ switch (buf_sz) { ++ case 0x400: ++ return UDMA_BUFSZ_CODE_1KB; ++ case 0x1000: ++ return UDMA_BUFSZ_CODE_4KB; ++ case 0x4000: ++ return UDMA_BUFSZ_CODE_16KB; ++ case 0x10000: ++ return UDMA_BUFSZ_CODE_64KB; ++ case 0x20000: ++ return UDMA_BUFSZ_CODE_128KB; ++ case 0x40000: ++ return UDMA_BUFSZ_CODE_256KB; ++ case 0x80000: ++ return UDMA_BUFSZ_CODE_512KB; ++ case 0x100000: ++ return UDMA_BUFSZ_CODE_1024KB; ++ case 0x200000: ++ return UDMA_BUFSZ_CODE_2048KB; ++ case 0x400000: ++ return UDMA_BUFSZ_CODE_4096KB; ++ case 0x800000: ++ return UDMA_BUFSZ_CODE_8192KB; ++ case 0x1000000: ++ return UDMA_BUFSZ_CODE_16384KB; ++ default: ++ return -1; ++ } ++ ++ return -1; ++} ++ ++static u32 aspeed_udma_get_tx_rptr(u32 ch_no) ++{ ++ return readl(udma->regs + UDMA_CHX_TX_RD_PTR(ch_no)); ++} ++ ++static u32 aspeed_udma_get_rx_wptr(u32 ch_no) ++{ ++ return readl(udma->regs + UDMA_CHX_RX_WR_PTR(ch_no)); ++} ++ ++static void aspeed_udma_set_ptr(u32 ch_no, u32 ptr, bool is_tx) ++{ ++ writel(ptr, udma->regs + ++ ((is_tx) ? ++ UDMA_CHX_TX_WR_PTR(ch_no) : ++ UDMA_CHX_RX_RD_PTR(ch_no))); ++} ++ ++void aspeed_udma_set_tx_wptr(u32 ch_no, u32 wptr) ++{ ++ aspeed_udma_set_ptr(ch_no, wptr, true); ++} ++EXPORT_SYMBOL(aspeed_udma_set_tx_wptr); ++ ++void aspeed_udma_set_rx_rptr(u32 ch_no, u32 rptr) ++{ ++ aspeed_udma_set_ptr(ch_no, rptr, false); ++} ++EXPORT_SYMBOL(aspeed_udma_set_rx_rptr); ++ ++static int aspeed_udma_free_chan(u32 ch_no, bool is_tx) ++{ ++ u32 reg; ++ unsigned long flags; ++ ++ if (ch_no > UDMA_MAX_CHANNEL) ++ return -EINVAL; ++ ++ spin_lock_irqsave(&udma->lock, flags); ++ ++ reg = readl(udma->regs + ++ ((is_tx) ? UDMA_TX_DMA_INT_EN : UDMA_RX_DMA_INT_EN)); ++ reg &= ~(0x1 << ch_no); ++ ++ writel(reg, udma->regs + ++ ((is_tx) ? UDMA_TX_DMA_INT_EN : UDMA_RX_DMA_INT_EN)); ++ ++ spin_unlock_irqrestore(&udma->lock, flags); ++ ++ return 0; ++} ++ ++int aspeed_udma_free_tx_chan(u32 ch_no) ++{ ++ return aspeed_udma_free_chan(ch_no, true); ++} ++EXPORT_SYMBOL(aspeed_udma_free_tx_chan); ++ ++int aspeed_udma_free_rx_chan(u32 ch_no) ++{ ++ return aspeed_udma_free_chan(ch_no, false); ++} ++EXPORT_SYMBOL(aspeed_udma_free_rx_chan); ++ ++static int aspeed_udma_request_chan(u32 ch_no, dma_addr_t addr, ++ struct circ_buf *rb, u32 rb_sz, ++ aspeed_udma_cb_t cb, void *id, bool dis_tmout, bool is_tx) ++{ ++ int retval = 0; ++ int rbsz_code; ++ ++ u32 reg; ++ unsigned long flags; ++ struct aspeed_udma_chan *ch; ++ ++ if (ch_no > UDMA_MAX_CHANNEL) { ++ retval = -EINVAL; ++ goto out; ++ } ++ ++ if (IS_ERR_OR_NULL(rb) || IS_ERR_OR_NULL(rb->buf)) { ++ retval = -EINVAL; ++ goto out; ++ } ++ ++ rbsz_code = aspeed_udma_get_bufsz_code(rb_sz); ++ if (rbsz_code < 0) { ++ retval = -EINVAL; ++ goto out; ++ } ++ ++ spin_lock_irqsave(&udma->lock, flags); ++ ++ if (is_tx) { ++ reg = readl(udma->regs + UDMA_TX_DMA_INT_EN); ++ if (reg & (0x1 << ch_no)) { ++ retval = -EBUSY; ++ goto unlock_n_out; ++ } ++ ++ reg |= (0x1 << ch_no); ++ writel(reg, udma->regs + UDMA_TX_DMA_INT_EN); ++ ++ reg = readl(udma->regs + UDMA_CHX_TX_CTRL(ch_no)); ++ reg |= (dis_tmout) ? UDMA_TX_CTRL_TMOUT_DISABLE : 0; ++ reg |= (rbsz_code << UDMA_TX_CTRL_BUFSZ_SHIFT) & UDMA_TX_CTRL_BUFSZ_MASK; ++ writel(reg, udma->regs + UDMA_CHX_TX_CTRL(ch_no)); ++ ++ writel(addr, udma->regs + UDMA_CHX_TX_BUF_BASE(ch_no)); ++ } ++ else { ++ reg = readl(udma->regs + UDMA_RX_DMA_INT_EN); ++ if (reg & (0x1 << ch_no)) { ++ retval = -EBUSY; ++ goto unlock_n_out; ++ } ++ ++ reg |= (0x1 << ch_no); ++ writel(reg, udma->regs + UDMA_RX_DMA_INT_EN); ++ ++ reg = readl(udma->regs + UDMA_CHX_RX_CTRL(ch_no)); ++ reg |= (dis_tmout) ? UDMA_RX_CTRL_TMOUT_DISABLE : 0; ++ reg |= (rbsz_code << UDMA_RX_CTRL_BUFSZ_SHIFT) & UDMA_RX_CTRL_BUFSZ_MASK; ++ writel(reg, udma->regs + UDMA_CHX_RX_CTRL(ch_no)); ++ ++ writel(addr, udma->regs + UDMA_CHX_RX_BUF_BASE(ch_no)); ++ } ++ ++ ch = (is_tx) ? &udma->tx_chs[ch_no] : &udma->rx_chs[ch_no]; ++ ch->rb = rb; ++ ch->rb_sz = rb_sz; ++ ch->cb = cb; ++ ch->cb_arg = id; ++ ch->dma_addr = addr; ++ ch->dis_tmout = dis_tmout; ++ ++unlock_n_out: ++ spin_unlock_irqrestore(&udma->lock, flags); ++out: ++ return 0; ++} ++ ++int aspeed_udma_request_tx_chan(u32 ch_no, dma_addr_t addr, ++ struct circ_buf *rb, u32 rb_sz, ++ aspeed_udma_cb_t cb, void *id, bool dis_tmout) ++{ ++ return aspeed_udma_request_chan(ch_no, addr, rb, rb_sz, cb, id, ++ dis_tmout, true); ++} ++EXPORT_SYMBOL(aspeed_udma_request_tx_chan); ++ ++int aspeed_udma_request_rx_chan(u32 ch_no, dma_addr_t addr, ++ struct circ_buf *rb, u32 rb_sz, ++ aspeed_udma_cb_t cb, void *id, bool dis_tmout) ++{ ++ return aspeed_udma_request_chan(ch_no, addr, rb, rb_sz, cb, id, ++ dis_tmout, false); ++} ++EXPORT_SYMBOL(aspeed_udma_request_rx_chan); ++ ++static void aspeed_udma_chan_ctrl(u32 ch_no, u32 op, bool is_tx) ++{ ++ unsigned long flags; ++ u32 reg_en, reg_rst; ++ u32 reg_en_off = (is_tx) ? UDMA_TX_DMA_EN : UDMA_RX_DMA_EN; ++ u32 reg_rst_off = (is_tx) ? UDMA_TX_DMA_RST : UDMA_TX_DMA_RST; ++ ++ if (ch_no > UDMA_MAX_CHANNEL) ++ return; ++ ++ spin_lock_irqsave(&udma->lock, flags); ++ ++ reg_en = readl(udma->regs + reg_en_off); ++ reg_rst = readl(udma->regs + reg_rst_off); ++ ++ switch (op) { ++ case ASPEED_UDMA_OP_ENABLE: ++ reg_en |= (0x1 << ch_no); ++ writel(reg_en, udma->regs + reg_en_off); ++ break; ++ case ASPEED_UDMA_OP_DISABLE: ++ reg_en &= ~(0x1 << ch_no); ++ writel(reg_en, udma->regs + reg_en_off); ++ break; ++ case ASPEED_UDMA_OP_RESET: ++ reg_en &= ~(0x1 << ch_no); ++ writel(reg_en, udma->regs + reg_en_off); ++ reg_rst |= (0x1 << ch_no); ++ writel(reg_rst, udma->regs + reg_rst_off); ++ reg_rst &= ~(0x1 << ch_no); ++ writel(reg_rst, udma->regs + reg_rst_off); ++ break; ++ default: ++ break; ++ } ++ ++ spin_unlock_irqrestore(&udma->lock, flags); ++} ++ ++void aspeed_udma_tx_chan_ctrl(u32 ch_no, enum aspeed_udma_ops op) ++{ ++ aspeed_udma_chan_ctrl(ch_no, op, true); ++} ++EXPORT_SYMBOL(aspeed_udma_tx_chan_ctrl); ++ ++void aspeed_udma_rx_chan_ctrl(u32 ch_no, enum aspeed_udma_ops op) ++{ ++ aspeed_udma_chan_ctrl(ch_no, op, false); ++} ++EXPORT_SYMBOL(aspeed_udma_rx_chan_ctrl); ++ ++static irqreturn_t aspeed_udma_isr(int irq, void *arg) ++{ ++ u32 bit; ++ unsigned long tx_stat = readl(udma->regs + UDMA_TX_DMA_INT_STAT); ++ unsigned long rx_stat = readl(udma->regs + UDMA_RX_DMA_INT_STAT); ++ ++ if (udma != (struct aspeed_udma *)arg) ++ return IRQ_NONE; ++ ++ if (tx_stat == 0 && rx_stat == 0) ++ return IRQ_NONE; ++ ++ for_each_set_bit(bit, &tx_stat, UDMA_MAX_CHANNEL) { ++ writel((0x1 << bit), udma->regs + UDMA_TX_DMA_INT_STAT); ++ if (udma->tx_chs[bit].cb) ++ udma->tx_chs[bit].cb(aspeed_udma_get_tx_rptr(bit), ++ udma->tx_chs[bit].cb_arg); ++ } ++ ++ for_each_set_bit(bit, &rx_stat, UDMA_MAX_CHANNEL) { ++ writel((0x1 << bit), udma->regs + UDMA_RX_DMA_INT_STAT); ++ if (udma->rx_chs[bit].cb) ++ udma->rx_chs[bit].cb(aspeed_udma_get_rx_wptr(bit), ++ udma->rx_chs[bit].cb_arg); ++ } ++ ++ return IRQ_HANDLED; ++} ++ ++static int aspeed_udma_probe(struct platform_device *pdev) ++{ ++ int i, rc; ++ struct resource *res; ++ struct device *dev = &pdev->dev; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (IS_ERR_OR_NULL(res)) { ++ dev_err(dev, "failed to get register base\n"); ++ return -ENODEV; ++ } ++ ++ udma->regs = devm_ioremap_resource(dev, res); ++ if (IS_ERR_OR_NULL(udma->regs)) { ++ dev_err(dev, "failed to map registers\n"); ++ return PTR_ERR(udma->regs); ++ } ++ ++ /* disable for safety */ ++ writel(0x0, udma->regs + UDMA_TX_DMA_EN); ++ writel(0x0, udma->regs + UDMA_RX_DMA_EN); ++ ++ udma->irq = platform_get_irq(pdev, 0); ++ if (udma->irq < 0) { ++ dev_err(dev, "failed to get IRQ number\n"); ++ return -ENODEV; ++ } ++ ++ rc = devm_request_irq(dev, udma->irq, aspeed_udma_isr, ++ IRQF_SHARED, DEVICE_NAME, udma); ++ if (rc) { ++ dev_err(dev, "failed to request IRQ handler\n"); ++ return rc; ++ } ++ ++ for (i = 0; i < UDMA_MAX_CHANNEL; ++i) { ++ writel(0, udma->regs + UDMA_CHX_TX_WR_PTR(i)); ++ writel(0, udma->regs + UDMA_CHX_RX_RD_PTR(i)); ++ } ++ ++ writel(0xffffffff, udma->regs + UDMA_TX_DMA_RST); ++ writel(0x0, udma->regs + UDMA_TX_DMA_RST); ++ ++ writel(0xffffffff, udma->regs + UDMA_RX_DMA_RST); ++ writel(0x0, udma->regs + UDMA_RX_DMA_RST); ++ ++ writel(0x0, udma->regs + UDMA_TX_DMA_INT_EN); ++ writel(0xffffffff, udma->regs + UDMA_TX_DMA_INT_STAT); ++ writel(0x0, udma->regs + UDMA_RX_DMA_INT_EN); ++ writel(0xffffffff, udma->regs + UDMA_RX_DMA_INT_STAT); ++ ++ writel(UDMA_TIMEOUT, udma->regs + UDMA_TIMEOUT_TIMER); ++ ++ spin_lock_init(&udma->lock); ++ ++ dev_set_drvdata(dev, udma); ++ ++ return 0; ++} ++ ++static const struct of_device_id aspeed_udma_match[] = { ++ { .compatible = "aspeed,ast2500-udma" }, ++ { .compatible = "aspeed,ast2600-udma" }, ++}; ++ ++static struct platform_driver aspeed_udma_driver = { ++ .driver = { ++ .name = DEVICE_NAME, ++ .of_match_table = aspeed_udma_match, ++ ++ }, ++ .probe = aspeed_udma_probe, ++}; ++ ++module_platform_driver(aspeed_udma_driver); ++ ++MODULE_AUTHOR("Chia-Wei Wang "); ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("Aspeed UDMA Engine Driver"); +diff --git a/drivers/tty/serial/8250/8250_aspeed.c b/drivers/tty/serial/8250/8250_aspeed.c +new file mode 100644 +index 000000000000..1dc798298fca +--- /dev/null ++++ b/drivers/tty/serial/8250/8250_aspeed.c +@@ -0,0 +1,494 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (C) ASPEED Technology Inc. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "8250.h" ++ ++#define DEVICE_NAME "aspeed-uart" ++ ++/* offsets for the aspeed virtual uart registers */ ++#define VUART_GCRA 0x20 ++#define VUART_GCRA_VUART_EN BIT(0) ++#define VUART_GCRA_SIRQ_POLARITY BIT(1) ++#define VUART_GCRA_DISABLE_HOST_TX_DISCARD BIT(5) ++#define VUART_GCRB 0x24 ++#define VUART_GCRB_HOST_SIRQ_MASK GENMASK(7, 4) ++#define VUART_GCRB_HOST_SIRQ_SHIFT 4 ++#define VUART_ADDRL 0x28 ++#define VUART_ADDRH 0x2c ++ ++#define DMA_TX_BUFSZ PAGE_SIZE ++#define DMA_RX_BUFSZ (64 * 1024) ++ ++struct uart_ops ast8250_pops; ++ ++struct ast8250_vuart { ++ u32 port; ++ u32 sirq; ++ u32 sirq_pol; ++}; ++ ++struct ast8250_udma { ++ u32 ch; ++ ++ u32 tx_rbsz; ++ u32 rx_rbsz; ++ ++ dma_addr_t tx_addr; ++ dma_addr_t rx_addr; ++ ++ struct circ_buf *tx_rb; ++ struct circ_buf *rx_rb; ++ ++ bool tx_tmout_dis; ++ bool rx_tmout_dis; ++}; ++ ++struct ast8250_data { ++ int line; ++ ++ u8 __iomem *regs; ++ ++ bool is_vuart; ++ bool use_dma; ++ ++ struct reset_control *rst; ++ struct clk *clk; ++ ++ struct ast8250_vuart vuart; ++ struct ast8250_udma dma; ++}; ++ ++static void ast8250_dma_tx_complete(int tx_rb_rptr, void *id) ++{ ++ u32 count; ++ unsigned long flags; ++ struct uart_port *port = (struct uart_port*)id; ++ struct ast8250_data *data = port->private_data; ++ ++ spin_lock_irqsave(&port->lock, flags); ++ ++ count = CIRC_CNT(tx_rb_rptr, port->state->xmit.tail, data->dma.tx_rbsz); ++ port->state->xmit.tail = tx_rb_rptr; ++ port->icount.tx += count; ++ ++ if (uart_circ_chars_pending(&port->state->xmit) < WAKEUP_CHARS) ++ uart_write_wakeup(port); ++ ++ spin_unlock_irqrestore(&port->lock, flags); ++} ++ ++static void ast8250_dma_rx_complete(int rx_rb_wptr, void *id) ++{ ++ unsigned long flags; ++ struct uart_port *up = (struct uart_port*)id; ++ struct tty_port *tp = &up->state->port; ++ struct ast8250_data *data = up->private_data; ++ struct ast8250_udma *dma = &data->dma; ++ struct circ_buf *rx_rb = dma->rx_rb; ++ u32 rx_rbsz = dma->rx_rbsz; ++ u32 count = 0; ++ ++ spin_lock_irqsave(&up->lock, flags); ++ ++ rx_rb->head = rx_rb_wptr; ++ ++ dma_sync_single_for_cpu(up->dev, ++ dma->rx_addr, dma->rx_rbsz, DMA_FROM_DEVICE); ++ ++ while (CIRC_CNT(rx_rb->head, rx_rb->tail, rx_rbsz)) { ++ count = CIRC_CNT_TO_END(rx_rb->head, rx_rb->tail, rx_rbsz); ++ ++ tty_insert_flip_string(tp, rx_rb->buf + rx_rb->tail, count); ++ ++ rx_rb->tail += count; ++ rx_rb->tail %= rx_rbsz; ++ ++ up->icount.rx += count; ++ } ++ ++ if (count) { ++ aspeed_udma_set_rx_rptr(data->dma.ch, rx_rb->tail); ++ tty_flip_buffer_push(tp); ++ } ++ ++ spin_unlock_irqrestore(&up->lock, flags); ++} ++ ++static void ast8250_dma_start_tx(struct uart_port *port) ++{ ++ struct ast8250_data *data = port->private_data; ++ struct ast8250_udma *dma = &data->dma; ++ struct circ_buf *tx_rb = dma->tx_rb; ++ ++ dma_sync_single_for_device(port->dev, ++ dma->tx_addr, dma->tx_rbsz, DMA_TO_DEVICE); ++ ++ aspeed_udma_set_tx_wptr(dma->ch, tx_rb->head); ++} ++ ++static void ast8250_dma_pops_hook(struct uart_port *port) ++{ ++ static int first = 1; ++ ++ if (first) { ++ ast8250_pops = *port->ops; ++ ast8250_pops.start_tx = ast8250_dma_start_tx; ++ } ++ ++ first = 0; ++ port->ops = &ast8250_pops; ++} ++ ++static void ast8250_vuart_init(struct ast8250_data *data) ++{ ++ u8 reg; ++ struct ast8250_vuart *vuart = &data->vuart; ++ ++ /* IO port address */ ++ writeb((u8)(vuart->port >> 0), data->regs + VUART_ADDRL); ++ writeb((u8)(vuart->port >> 8), data->regs + VUART_ADDRH); ++ ++ /* SIRQ number */ ++ reg = readb(data->regs + VUART_GCRB); ++ reg &= ~VUART_GCRB_HOST_SIRQ_MASK; ++ reg |= ((vuart->sirq << VUART_GCRB_HOST_SIRQ_SHIFT) & VUART_GCRB_HOST_SIRQ_MASK); ++ writeb(reg, data->regs + VUART_GCRB); ++ ++ /* SIRQ polarity */ ++ reg = readb(data->regs + VUART_GCRA); ++ if (vuart->sirq_pol) ++ reg |= VUART_GCRA_SIRQ_POLARITY; ++ else ++ reg &= ~VUART_GCRA_SIRQ_POLARITY; ++ writeb(reg, data->regs + VUART_GCRA); ++} ++ ++static void ast8250_vuart_set_host_tx_discard(struct ast8250_data *data, bool discard) ++{ ++ u8 reg; ++ ++ reg = readb(data->regs + VUART_GCRA); ++ if (discard) ++ reg &= ~VUART_GCRA_DISABLE_HOST_TX_DISCARD; ++ else ++ reg |= VUART_GCRA_DISABLE_HOST_TX_DISCARD; ++ writeb(reg, data->regs + VUART_GCRA); ++} ++ ++static void ast8250_vuart_set_enable(struct ast8250_data *data, bool enable) ++{ ++ u8 reg; ++ ++ reg = readb(data->regs + VUART_GCRA); ++ if (enable) ++ reg |= VUART_GCRA_VUART_EN; ++ else ++ reg &= ~VUART_GCRA_VUART_EN; ++ writeb(reg, data->regs + VUART_GCRA); ++} ++ ++static int ast8250_handle_irq(struct uart_port *port) ++{ ++ u32 iir = port->serial_in(port, UART_IIR); ++ return serial8250_handle_irq(port, iir); ++} ++ ++static int ast8250_startup(struct uart_port *port) ++{ ++ int rc = 0; ++ struct ast8250_data *data = port->private_data; ++ struct ast8250_udma *dma; ++ ++ if (data->is_vuart) ++ ast8250_vuart_set_host_tx_discard(data, false); ++ ++ if (data->use_dma) { ++ dma = &data->dma; ++ ++ dma->tx_rbsz = DMA_TX_BUFSZ; ++ dma->rx_rbsz = DMA_RX_BUFSZ; ++ ++ /* ++ * We take the xmit buffer passed from upper layers as ++ * the DMA TX buffer and allocate a new buffer for the ++ * RX use. ++ * ++ * To keep the TX/RX operation consistency, we use the ++ * streaming DMA interface instead of the coherent one ++ */ ++ dma->tx_rb = &port->state->xmit; ++ dma->rx_rb->buf = kzalloc(data->dma.rx_rbsz, GFP_KERNEL); ++ if (IS_ERR_OR_NULL(dma->rx_rb->buf)) { ++ dev_err(port->dev, "failed to allcoate RX DMA buffer\n"); ++ rc = -ENOMEM; ++ goto out; ++ } ++ ++ dma->tx_addr = dma_map_single(port->dev, dma->tx_rb->buf, ++ dma->tx_rbsz, DMA_TO_DEVICE); ++ if (dma_mapping_error(port->dev, dma->tx_addr)) { ++ dev_err(port->dev, "failed to map streaming TX DMA region\n"); ++ rc = -ENOMEM; ++ goto free_dma_n_out; ++ } ++ ++ dma->rx_addr = dma_map_single(port->dev, dma->rx_rb->buf, ++ dma->rx_rbsz, DMA_FROM_DEVICE); ++ if (dma_mapping_error(port->dev, dma->rx_addr)) { ++ dev_err(port->dev, "failed to map streaming RX DMA region\n"); ++ rc = -ENOMEM; ++ goto free_dma_n_out; ++ } ++ ++ rc = aspeed_udma_request_tx_chan(dma->ch, dma->tx_addr, ++ dma->tx_rb, dma->tx_rbsz, ast8250_dma_tx_complete, port, dma->tx_tmout_dis); ++ if (rc) { ++ dev_err(port->dev, "failed to request DMA TX channel\n"); ++ goto free_dma_n_out; ++ } ++ ++ rc = aspeed_udma_request_rx_chan(dma->ch, dma->rx_addr, ++ dma->rx_rb, dma->rx_rbsz, ast8250_dma_rx_complete, port, dma->tx_tmout_dis); ++ if (rc) { ++ dev_err(port->dev, "failed to request DMA RX channel\n"); ++ goto free_dma_n_out; ++ } ++ ++ ast8250_dma_pops_hook(port); ++ ++ aspeed_udma_tx_chan_ctrl(dma->ch, ASPEED_UDMA_OP_ENABLE); ++ aspeed_udma_rx_chan_ctrl(dma->ch, ASPEED_UDMA_OP_ENABLE); ++ } ++ ++ memset(&port->icount, 0, sizeof(port->icount)); ++ return serial8250_do_startup(port); ++ ++free_dma_n_out: ++ kfree(dma->rx_rb->buf); ++out: ++ return rc; ++} ++ ++static void ast8250_shutdown(struct uart_port *port) ++{ ++ int rc; ++ struct ast8250_data *data = port->private_data; ++ struct ast8250_udma *dma; ++ ++ if (data->use_dma) { ++ dma = &data->dma; ++ ++ aspeed_udma_tx_chan_ctrl(dma->ch, ASPEED_UDMA_OP_DISABLE); ++ aspeed_udma_rx_chan_ctrl(dma->ch, ASPEED_UDMA_OP_DISABLE); ++ ++ rc = aspeed_udma_free_tx_chan(dma->ch); ++ if (rc) ++ dev_err(port->dev, "failed to free DMA TX channel, rc=%d\n", rc); ++ ++ rc = aspeed_udma_free_rx_chan(dma->ch); ++ if (rc) ++ dev_err(port->dev, "failed to free DMA TX channel, rc=%d\n", rc); ++ ++ dma_unmap_single(port->dev, dma->tx_addr, ++ dma->tx_rbsz, DMA_TO_DEVICE); ++ dma_unmap_single(port->dev, dma->rx_addr, ++ dma->rx_rbsz, DMA_FROM_DEVICE); ++ ++ if (dma->rx_rb->buf) ++ kfree(dma->rx_rb->buf); ++ } ++ ++ if (data->is_vuart) ++ ast8250_vuart_set_host_tx_discard(data, true); ++ ++ serial8250_do_shutdown(port); ++} ++ ++static int __maybe_unused ast8250_suspend(struct device *dev) ++{ ++ struct ast8250_data *data = dev_get_drvdata(dev); ++ serial8250_suspend_port(data->line); ++ return 0; ++} ++ ++static int __maybe_unused ast8250_resume(struct device *dev) ++{ ++ struct ast8250_data *data = dev_get_drvdata(dev); ++ serial8250_resume_port(data->line); ++ return 0; ++} ++ ++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; ++ struct ast8250_data *data; ++ ++ struct resource *res; ++ u32 irq; ++ ++ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); ++ if (data == NULL) ++ return -ENOMEM; ++ ++ data->dma.rx_rb = devm_kzalloc(dev, sizeof(data->dma.rx_rb), GFP_KERNEL); ++ if (data->dma.rx_rb == NULL) ++ return -ENOMEM; ++ ++ irq = platform_get_irq(pdev, 0); ++ if (irq < 0) { ++ if (irq != -EPROBE_DEFER) ++ dev_err(dev, "failed to get IRQ number\n"); ++ return irq; ++ } ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (res == NULL) { ++ dev_err(dev, "failed to get register base\n"); ++ return -ENODEV; ++ } ++ ++ data->regs = devm_ioremap(dev, res->start, resource_size(res)); ++ if (IS_ERR(data->regs)) { ++ dev_err(dev, "failed to map registers\n"); ++ return PTR_ERR(data->regs); ++ } ++ ++ data->clk = devm_clk_get(dev, NULL); ++ if (IS_ERR(data->clk)) { ++ dev_err(dev, "failed to get clocks\n"); ++ return -ENODEV; ++ } ++ ++ rc = clk_prepare_enable(data->clk); ++ if (rc) { ++ dev_err(dev, "failed to enable clock\n"); ++ return rc; ++ } ++ ++ data->rst = devm_reset_control_get_optional_exclusive(dev, NULL); ++ if (!IS_ERR(data->rst)) ++ reset_control_deassert(data->rst); ++ ++ data->is_vuart = of_property_read_bool(dev->of_node, "virtual"); ++ if (data->is_vuart) { ++ rc = of_property_read_u32(dev->of_node, "port", &data->vuart.port); ++ if (rc) { ++ dev_err(dev, "failed to get VUART port address\n"); ++ return -ENODEV; ++ } ++ ++ rc = of_property_read_u32(dev->of_node, "sirq", &data->vuart.sirq); ++ if (rc) { ++ dev_err(dev, "failed to get VUART SIRQ number\n"); ++ return -ENODEV; ++ } ++ ++ rc = of_property_read_u32(dev->of_node, "sirq-polarity", &data->vuart.sirq_pol); ++ if (rc) { ++ dev_err(dev, "failed to get VUART SIRQ polarity\n"); ++ return -ENODEV; ++ } ++ ++ ast8250_vuart_init(data); ++ ast8250_vuart_set_host_tx_discard(data, true); ++ ast8250_vuart_set_enable(data, true); ++ } ++ ++ data->use_dma = of_property_read_bool(dev->of_node, "dma-mode"); ++ if (data->use_dma) { ++ rc = of_property_read_u32(dev->of_node, "dma-channel", &data->dma.ch); ++ if (rc) { ++ dev_err(dev, "failed to get DMA channel\n"); ++ return -ENODEV; ++ } ++ ++ data->dma.tx_tmout_dis = of_property_read_bool(dev->of_node, "dma-tx-timeout-disable"); ++ data->dma.rx_tmout_dis = of_property_read_bool(dev->of_node, "dma-rx-timeout-disable"); ++ } ++ ++ spin_lock_init(&port->lock); ++ port->dev = dev; ++ port->type = PORT_16550; ++ port->irq = irq; ++ port->line = of_alias_get_id(dev->of_node, "serial"); ++ port->handle_irq = ast8250_handle_irq; ++ port->mapbase = res->start; ++ port->mapsize = resource_size(res); ++ port->membase = data->regs; ++ port->uartclk = clk_get_rate(data->clk); ++ port->regshift = 2; ++ port->iotype = UPIO_MEM32; ++ port->flags = UPF_FIXED_PORT | UPF_SHARE_IRQ; ++ port->startup = ast8250_startup; ++ port->shutdown = ast8250_shutdown; ++ port->private_data = data; ++ ++ data->line = serial8250_register_8250_port(&uart); ++ if (data->line < 0) { ++ dev_err(dev, "failed to register 8250 port\n"); ++ return data->line; ++ } ++ ++ pm_runtime_set_active(&pdev->dev); ++ pm_runtime_enable(&pdev->dev); ++ ++ platform_set_drvdata(pdev, data); ++ return 0; ++} ++ ++static int ast8250_remove(struct platform_device *pdev) ++{ ++ struct ast8250_data *data = platform_get_drvdata(pdev); ++ ++ if (data->is_vuart) ++ ast8250_vuart_set_enable(data, false); ++ ++ serial8250_unregister_port(data->line); ++ return 0; ++} ++ ++static const struct dev_pm_ops ast8250_pm_ops = { ++ SET_SYSTEM_SLEEP_PM_OPS(ast8250_suspend, ast8250_resume) ++}; ++ ++static const struct of_device_id ast8250_of_match[] = { ++ { .compatible = "aspeed,ast2500-uart" }, ++ { .compatible = "aspeed,ast2600-uart" }, ++}; ++ ++static struct platform_driver ast8250_platform_driver = { ++ .driver = { ++ .name = DEVICE_NAME, ++ .pm = &ast8250_pm_ops, ++ .of_match_table = ast8250_of_match, ++ }, ++ .probe = ast8250_probe, ++ .remove = ast8250_remove, ++}; ++ ++module_platform_driver(ast8250_platform_driver); ++ ++MODULE_AUTHOR("Chia-Wei Wang "); ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("Aspeed UART Driver"); +diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig +index d1b3c2373fa4..b62a972c9c97 100644 +--- a/drivers/tty/serial/8250/Kconfig ++++ b/drivers/tty/serial/8250/Kconfig +@@ -249,6 +249,14 @@ config SERIAL_8250_ACCENT + To compile this driver as a module, choose M here: the module + will be called 8250_accent. + ++config SERIAL_8250_ASPEED ++ tristate "Aspeed serial port support" ++ depends on SERIAL_8250 && ARCH_ASPEED ++ select ASPEED_UDMA ++ help ++ If you have a system using an Aspeed ASTXXXX SoCs and wish to make use ++ of its UARTs, say Y to this option. If unsure, say N. ++ + config SERIAL_8250_ASPEED_VUART + tristate "Aspeed Virtual UART" + depends on SERIAL_8250 +diff --git a/drivers/tty/serial/8250/Makefile b/drivers/tty/serial/8250/Makefile +index b9bcd73c8997..310375fe8d7e 100644 +--- a/drivers/tty/serial/8250/Makefile ++++ b/drivers/tty/serial/8250/Makefile +@@ -16,6 +16,7 @@ obj-$(CONFIG_SERIAL_8250_EXAR) += 8250_exar.o + obj-$(CONFIG_SERIAL_8250_HP300) += 8250_hp300.o + obj-$(CONFIG_SERIAL_8250_CS) += serial_cs.o + obj-$(CONFIG_SERIAL_8250_ACORN) += 8250_acorn.o ++obj-$(CONFIG_SERIAL_8250_ASPEED) += 8250_aspeed.o + obj-$(CONFIG_SERIAL_8250_ASPEED_VUART) += 8250_aspeed_vuart.o + obj-$(CONFIG_SERIAL_8250_BCM2835AUX) += 8250_bcm2835aux.o + obj-$(CONFIG_SERIAL_8250_CONSOLE) += 8250_early.o +diff --git a/include/linux/soc/aspeed/aspeed-udma.h b/include/linux/soc/aspeed/aspeed-udma.h +new file mode 100644 +index 000000000000..33acea745f1c +--- /dev/null ++++ b/include/linux/soc/aspeed/aspeed-udma.h +@@ -0,0 +1,30 @@ ++#ifndef __ASPEED_UDMA_H__ ++#define __ASPEED_UDMA_H__ ++ ++#include ++ ++typedef void (*aspeed_udma_cb_t)(int rb_rwptr, void *id); ++ ++enum aspeed_udma_ops { ++ ASPEED_UDMA_OP_ENABLE, ++ ASPEED_UDMA_OP_DISABLE, ++ ASPEED_UDMA_OP_RESET, ++}; ++ ++void aspeed_udma_set_tx_wptr(u32 ch_no, u32 wptr); ++void aspeed_udma_set_rx_rptr(u32 ch_no, u32 rptr); ++ ++void aspeed_udma_tx_chan_ctrl(u32 ch_no, enum aspeed_udma_ops op); ++void aspeed_udma_rx_chan_ctrl(u32 ch_no, enum aspeed_udma_ops op); ++ ++int aspeed_udma_request_tx_chan(u32 ch_no, dma_addr_t addr, ++ struct circ_buf *rb, u32 rb_sz, ++ aspeed_udma_cb_t cb, void *id, bool en_tmout); ++int aspeed_udma_request_rx_chan(u32 ch_no, dma_addr_t addr, ++ struct circ_buf *rb, u32 rb_sz, ++ aspeed_udma_cb_t cb, void *id, bool en_tmout); ++ ++int aspeed_udma_free_tx_chan(u32 ch_no); ++int aspeed_udma_free_rx_chan(u32 ch_no); ++ ++#endif +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-ast2600/recipes-kernel/linux/linux-aspeed/0002-serial-8250-Fix-RX-DMA-time-out-property.patch b/meta-openbmc-mods/meta-ast2600/recipes-kernel/linux/linux-aspeed/0002-serial-8250-Fix-RX-DMA-time-out-property.patch new file mode 100644 index 000000000..bbadb8b01 --- /dev/null +++ b/meta-openbmc-mods/meta-ast2600/recipes-kernel/linux/linux-aspeed/0002-serial-8250-Fix-RX-DMA-time-out-property.patch @@ -0,0 +1,29 @@ +From 64897e97edeefc7fbc89b07aaece760009ba76a0 Mon Sep 17 00:00:00 2001 +From: "Chia-Wei, Wang" +Date: Mon, 21 Dec 2020 16:27:20 +0800 +Subject: [PATCH] serial: 8250: Fix RX DMA time out property + +Fix the typo that passing the TX DMA time out property +to the RX configuration. + +Signed-off-by: Chia-Wei, Wang +--- + drivers/tty/serial/8250/8250_aspeed.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/tty/serial/8250/8250_aspeed.c b/drivers/tty/serial/8250/8250_aspeed.c +index 1dc798298fca..fc14c86d3761 100644 +--- a/drivers/tty/serial/8250/8250_aspeed.c ++++ b/drivers/tty/serial/8250/8250_aspeed.c +@@ -266,7 +266,7 @@ static int ast8250_startup(struct uart_port *port) + } + + rc = aspeed_udma_request_rx_chan(dma->ch, dma->rx_addr, +- dma->rx_rb, dma->rx_rbsz, ast8250_dma_rx_complete, port, dma->tx_tmout_dis); ++ dma->rx_rb, dma->rx_rbsz, ast8250_dma_rx_complete, port, dma->rx_tmout_dis); + if (rc) { + dev_err(port->dev, "failed to request DMA RX channel\n"); + goto free_dma_n_out; +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-ast2600/recipes-kernel/linux/linux-aspeed/0003-serial-8250_aspeed-Make-port-type-fixed.patch b/meta-openbmc-mods/meta-ast2600/recipes-kernel/linux/linux-aspeed/0003-serial-8250_aspeed-Make-port-type-fixed.patch new file mode 100644 index 000000000..4a0e221b6 --- /dev/null +++ b/meta-openbmc-mods/meta-ast2600/recipes-kernel/linux/linux-aspeed/0003-serial-8250_aspeed-Make-port-type-fixed.patch @@ -0,0 +1,57 @@ +From a7a8bf2f7df17f69a407abe21a14b3f0f2c7338c Mon Sep 17 00:00:00 2001 +From: Chia-Wei Wang +Date: Mon, 31 May 2021 15:39:59 +0800 +Subject: [PATCH] serial/8250_aspeed: Make port type fixed + +Make the UART port type fixed to 16550A to +avoid redundant probing. + +Signed-off-by: Chia-Wei Wang +Change-Id: Id179725b1cd475cd52c18c43b6c312dcd912fc20 +--- + drivers/tty/serial/8250/8250_aspeed.c | 14 +++++++------- + 1 file changed, 7 insertions(+), 7 deletions(-) + +diff --git a/drivers/tty/serial/8250/8250_aspeed.c b/drivers/tty/serial/8250/8250_aspeed.c +index fc14c86d3761..bb3955bb8c24 100644 +--- a/drivers/tty/serial/8250/8250_aspeed.c ++++ b/drivers/tty/serial/8250/8250_aspeed.c +@@ -25,12 +25,12 @@ + + /* offsets for the aspeed virtual uart registers */ + #define VUART_GCRA 0x20 +-#define VUART_GCRA_VUART_EN BIT(0) +-#define VUART_GCRA_SIRQ_POLARITY BIT(1) +-#define VUART_GCRA_DISABLE_HOST_TX_DISCARD BIT(5) ++#define VUART_GCRA_VUART_EN BIT(0) ++#define VUART_GCRA_SIRQ_POLARITY BIT(1) ++#define VUART_GCRA_DISABLE_HOST_TX_DISCARD BIT(5) + #define VUART_GCRB 0x24 +-#define VUART_GCRB_HOST_SIRQ_MASK GENMASK(7, 4) +-#define VUART_GCRB_HOST_SIRQ_SHIFT 4 ++#define VUART_GCRB_HOST_SIRQ_MASK GENMASK(7, 4) ++#define VUART_GCRB_HOST_SIRQ_SHIFT 4 + #define VUART_ADDRL 0x28 + #define VUART_ADDRH 0x2c + +@@ -429,7 +429,7 @@ static int ast8250_probe(struct platform_device *pdev) + + spin_lock_init(&port->lock); + port->dev = dev; +- port->type = PORT_16550; ++ port->type = PORT_16550A; + port->irq = irq; + port->line = of_alias_get_id(dev->of_node, "serial"); + port->handle_irq = ast8250_handle_irq; +@@ -439,7 +439,7 @@ static int ast8250_probe(struct platform_device *pdev) + port->uartclk = clk_get_rate(data->clk); + port->regshift = 2; + port->iotype = UPIO_MEM32; +- port->flags = UPF_FIXED_PORT | UPF_SHARE_IRQ; ++ port->flags = UPF_FIXED_TYPE | UPF_FIXED_PORT | UPF_SHARE_IRQ; + port->startup = ast8250_startup; + port->shutdown = ast8250_shutdown; + port->private_data = data; +-- +2.17.1 + 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 +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 | 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 + #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 + */ +@@ -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 + diff --git a/meta-openbmc-mods/meta-ast2600/recipes-kernel/linux/linux-aspeed/intel-ast2600.cfg b/meta-openbmc-mods/meta-ast2600/recipes-kernel/linux/linux-aspeed/intel-ast2600.cfg index 4770e5825..9bd1535be 100644 --- a/meta-openbmc-mods/meta-ast2600/recipes-kernel/linux/linux-aspeed/intel-ast2600.cfg +++ b/meta-openbmc-mods/meta-ast2600/recipes-kernel/linux/linux-aspeed/intel-ast2600.cfg @@ -4,7 +4,6 @@ CONFIG_SPI_ASPEED_SMC=y CONFIG_SPI_FMC=y CONFIG_I3C=y CONFIG_DW_I3C_MASTER=y -CONFIG_ASPEED_I3C_MASTER=y CONFIG_I3CDEV=y CONFIG_U_SERIAL_CONSOLE=n CONFIG_HIGHMEM=n @@ -19,3 +18,6 @@ CONFIG_SUSPEND=n CONFIG_ASPEED_MCTP=y CONFIG_KERNEL_LZO=n CONFIG_KERNEL_XZ=y +CONFIG_SERIAL_8250_DW=n +CONFIG_SERIAL_8250_ASPEED=y +CONFIG_ASPEED_UDMA=y diff --git a/meta-openbmc-mods/meta-ast2600/recipes-kernel/linux/linux-aspeed_%.bbappend b/meta-openbmc-mods/meta-ast2600/recipes-kernel/linux/linux-aspeed_%.bbappend index e5687d4d1..df3901d77 100644 --- a/meta-openbmc-mods/meta-ast2600/recipes-kernel/linux/linux-aspeed_%.bbappend +++ b/meta-openbmc-mods/meta-ast2600/recipes-kernel/linux/linux-aspeed_%.bbappend @@ -3,5 +3,8 @@ FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:" SRC_URI += " \ file://intel-ast2600.cfg \ - file://0001-Add-a-workaround-to-cover-UART-interrupt-bug-in-AST2.patch \ + file://0001-serial-8250-Add-Aspeed-UART-driver-with-DMA-supporte.patch \ + file://0002-serial-8250-Fix-RX-DMA-time-out-property.patch \ + file://0003-serial-8250_aspeed-Make-port-type-fixed.patch \ + file://0004-Add-a-workaround-to-cover-UART-interrupt-bug-in-AST2.patch \ " -- cgit v1.2.3