Possibly the uart_*_* are full uart with more than 2 pins? Then we have to create lite defines with only rx/tx to not clash with other functions.
The tops_uart is unrelated (but the typo is still in upstreamed driver) but you should use the uart-lite to map only 2 pins for uart. Have you tried my change above and using it in dts?
yes, the issue i am having at the moment is with RX (canāt type in the console)⦠voltage is ~3.4v on both tx/rx pins ⦠however can see any voltage rx fluctuation if I sent any characters to the consoleā¦debugging at the moment
so, finally got uart2 to work both on rx/tx ⦠there is a software/pinctrl configuration issue. The RX pad was toggling electrically, but the mux state was drifting out of uart2_3_lite, so the UART block never saw RX until I forced the pinctrl state.
This is ikely a conflicting pinctrl consumer or missing ākeep UART pins as defaultā in DTS.
I had to create a patch for 8250.mtk.c that forced the pins in UART mode. I also included monitoring that was useful to confirm it wasnāt an hardware fault (typing break on puty i could see the the pins state changing. See the patch below in case anyone needs it in the future
Force the uart2 mode for gpio 58 / 59 in UART mode echo 1 > /sys/kernel/debug/mtk8250/11000200.serial/force_pins
Enable monitoring (this can become quite verbose) echo 1 > /sys/kernel/debug/mtk8250/11000200.serial/rx_watch
--- a/drivers/tty/serial/8250/8250_mtk.c
+++ b/drivers/tty/serial/8250/8250_mtk.c
@@ -7,9 +7,11 @@
*/
#include <linux/clk.h>
#include <linux/io.h>
+#include <linux/debugfs.h>
#include <linux/module.h>
#include <linux/of_irq.h>
#include <linux/of_platform.h>
+#include <linux/of.h>
#include <linux/pinctrl/consumer.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
@@ -17,6 +19,9 @@
#include <linux/serial_reg.h>
#include <linux/console.h>
#include <linux/dma-mapping.h>
+#include <linux/jiffies.h>
+#include <linux/seq_file.h>
+#include <linux/workqueue.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
@@ -57,6 +62,18 @@
#define MTK_UART_XON1 40 /* I/O: Xon character 1 */
#define MTK_UART_XOFF1 42 /* I/O: Xoff character 1 */
+#define MT7988_GPIO_BASE 0x1001f000
+#define MT7988_GPIO_SIZE 0x1000
+#define MT7988_GPIO_MODE_7 0x370
+#define MT7988_GPIO_DI0 0x200
+#define MT7988_GPIO_DI 0x210
+#define MT7988_GPIO_DI2 0x220
+#define MT7988_GPIO_DIR 0x010
+#define MT7988_IOCFG_TR_BASE 0x11c10000
+#define MT7988_IOCFG_TR_SIZE 0x1000
+#define MT7988_IOCFG_TR_IES 0x40
+#define MT7988_IOCFG_TR_SMT 0xe0
+
#ifdef CONFIG_SERIAL_8250_DMA
enum dma_rx_status {
DMA_RX_START = 0,
@@ -75,6 +92,20 @@ struct mtk8250_data {
#ifdef CONFIG_SERIAL_8250_DMA
enum dma_rx_status rx_status;
#endif
+#if IS_ENABLED(CONFIG_DEBUG_FS)
+ struct dentry *debugfs_dir;
+ void __iomem *gpio_base;
+ void __iomem *iocfg_tr_base;
+ struct device *dev;
+ struct delayed_work rx_work;
+ bool rx_watch;
+ bool force_pins;
+ u8 last_lsr;
+ u32 last_di0;
+ u32 last_di1;
+ u32 last_di2;
+ u32 last_gpio_mode;
+#endif
int rx_wakeup_irq;
};
@@ -426,6 +457,264 @@ mtk8250_set_termios(struct uart_port *po
tty_termios_encode_baud_rate(termios, baud, baud);
}
+#if IS_ENABLED(CONFIG_DEBUG_FS)
+static struct dentry *mtk8250_debugfs_root;
+
+static int mtk8250_debugfs_show(struct seq_file *s, void *unused)
+{
+ struct uart_8250_port *up = s->private;
+ struct mtk8250_data *data = up->port.private_data;
+ unsigned long flags;
+ u32 val;
+
+ uart_port_lock_irqsave(&up->port, &flags);
+
+ seq_printf(s, "line=%d mapbase=%pa uartclk=%u regshift=%u\n",
+ data->line, &up->port.mapbase, up->port.uartclk,
+ up->port.regshift);
+ seq_printf(s, "LSR=%#x IIR=%#x IER=%#x LCR=%#x MCR=%#x MSR=%#x SCR=%#x\n",
+ serial_in(up, UART_LSR),
+ serial_in(up, UART_IIR),
+ serial_in(up, UART_IER),
+ serial_in(up, UART_LCR),
+ serial_in(up, UART_MCR),
+ serial_in(up, UART_MSR),
+ serial_in(up, UART_SCR));
+ seq_printf(s, "HIGHS=%#x SAMPLE_COUNT=%#x SAMPLE_POINT=%#x RATE_FIX=%#x\n",
+ serial_in(up, MTK_UART_HIGHS),
+ serial_in(up, MTK_UART_SAMPLE_COUNT),
+ serial_in(up, MTK_UART_SAMPLE_POINT),
+ serial_in(up, MTK_UART_RATE_FIX));
+ seq_printf(s, "DMA_EN=%#x DEBUG0=%#x\n",
+ serial_in(up, MTK_UART_DMA_EN),
+ serial_in(up, MTK_UART_DEBUG0));
+
+ uart_port_unlock_irqrestore(&up->port, flags);
+
+ if (data->gpio_base) {
+ val = readl(data->gpio_base + MT7988_GPIO_MODE_7);
+ seq_printf(s, "gpio_mode=0x%08x mode58=%u mode59=%u\n",
+ val, (val >> 8) & 0xf, (val >> 12) & 0xf);
+ val = readl(data->gpio_base + MT7988_GPIO_DI);
+ seq_printf(s, "gpio_di=0x%08x di58=%u di59=%u\n",
+ val, (val >> 26) & 1, (val >> 27) & 1);
+ val = readl(data->gpio_base + MT7988_GPIO_DIR);
+ seq_printf(s, "gpio_dir=0x%08x dir58=%u dir59=%u\n",
+ val, (val >> 26) & 1, (val >> 27) & 1);
+ }
+
+ if (data->iocfg_tr_base) {
+ val = readl(data->iocfg_tr_base + MT7988_IOCFG_TR_IES);
+ seq_printf(s, "iocfg_tr_ies=0x%08x ies58=%u ies59=%u\n",
+ val, (val >> 4) & 1, (val >> 5) & 1);
+ val = readl(data->iocfg_tr_base + MT7988_IOCFG_TR_SMT);
+ seq_printf(s, "iocfg_tr_smt=0x%08x smt58=%u smt59=%u\n",
+ val, (val >> 4) & 1, (val >> 5) & 1);
+ }
+
+ return 0;
+}
+
+static void mtk8250_rx_watch_work(struct work_struct *work)
+{
+ struct mtk8250_data *data = container_of(to_delayed_work(work),
+ struct mtk8250_data, rx_work);
+ struct uart_8250_port *up;
+ unsigned long flags;
+ int ret;
+ u8 lsr;
+ u32 di0 = 0;
+ u32 di1 = 0;
+ u32 di2 = 0;
+ u32 mode = 0;
+ u32 chg0 = 0;
+ u32 chg1 = 0;
+ u32 chg2 = 0;
+ u32 chg_mode = 0;
+ bool di_valid = false;
+ bool mode_valid = false;
+
+ if (!data->rx_watch)
+ return;
+
+ if (data->force_pins && data->dev) {
+ ret = pinctrl_pm_select_default_state(data->dev);
+ if (ret)
+ dev_warn_ratelimited(data->dev,
+ "pinctrl default state failed: %d\n",
+ ret);
+ }
+
+ up = serial8250_get_port(data->line);
+ if (!up)
+ goto resched;
+
+ uart_port_lock_irqsave(&up->port, &flags);
+ lsr = serial_in(up, UART_LSR);
+ uart_port_unlock_irqrestore(&up->port, flags);
+
+ if (data->gpio_base) {
+ mode = readl(data->gpio_base + MT7988_GPIO_MODE_7);
+ di0 = readl(data->gpio_base + MT7988_GPIO_DI0);
+ di1 = readl(data->gpio_base + MT7988_GPIO_DI);
+ di2 = readl(data->gpio_base + MT7988_GPIO_DI2);
+ chg0 = di0 ^ data->last_di0;
+ chg1 = di1 ^ data->last_di1;
+ chg2 = di2 ^ data->last_di2;
+ chg_mode = mode ^ data->last_gpio_mode;
+ di_valid = true;
+ mode_valid = true;
+ }
+
+ if (lsr != data->last_lsr ||
+ (di_valid && (chg0 || chg1 || chg2)) ||
+ (mode_valid && chg_mode)) {
+ dev_info(up->port.dev,
+ "uart%d LSR %#x -> %#x DI58=%u DI59=%u MODE=0x%08x M58=%u M59=%u DIchg0=%#x DIchg1=%#x DIchg2=%#x\n",
+ data->line, data->last_lsr, lsr,
+ (di1 >> 26) & 0x1, (di1 >> 27) & 0x1,
+ mode, (mode >> 8) & 0xf, (mode >> 12) & 0xf,
+ chg0, chg1, chg2);
+ data->last_lsr = lsr;
+ if (di_valid) {
+ data->last_di0 = di0;
+ data->last_di1 = di1;
+ data->last_di2 = di2;
+ }
+ if (mode_valid)
+ data->last_gpio_mode = mode;
+ }
+
+resched:
+ schedule_delayed_work(&data->rx_work, msecs_to_jiffies(100));
+}
+
+static int mtk8250_rx_watch_get(void *arg, u64 *val)
+{
+ struct mtk8250_data *data = arg;
+
+ *val = data->rx_watch;
+ return 0;
+}
+
+static int mtk8250_rx_watch_set(void *arg, u64 val)
+{
+ struct mtk8250_data *data = arg;
+ struct uart_8250_port *up;
+ unsigned long flags;
+ u8 lsr;
+ u32 di0 = 0;
+ u32 di1 = 0;
+ u32 di2 = 0;
+ u32 mode = 0;
+
+ if (val && !data->rx_watch) {
+ data->rx_watch = true;
+ up = serial8250_get_port(data->line);
+ if (up) {
+ uart_port_lock_irqsave(&up->port, &flags);
+ lsr = serial_in(up, UART_LSR);
+ uart_port_unlock_irqrestore(&up->port, flags);
+ data->last_lsr = lsr;
+ }
+ if (data->gpio_base) {
+ mode = readl(data->gpio_base + MT7988_GPIO_MODE_7);
+ di0 = readl(data->gpio_base + MT7988_GPIO_DI0);
+ di1 = readl(data->gpio_base + MT7988_GPIO_DI);
+ di2 = readl(data->gpio_base + MT7988_GPIO_DI2);
+ data->last_di0 = di0;
+ data->last_di1 = di1;
+ data->last_di2 = di2;
+ data->last_gpio_mode = mode;
+ }
+ schedule_delayed_work(&data->rx_work, 0);
+ } else if (!val && data->rx_watch) {
+ data->rx_watch = false;
+ cancel_delayed_work_sync(&data->rx_work);
+ }
+
+ return 0;
+}
+
+static int mtk8250_force_pins_get(void *arg, u64 *val)
+{
+ struct mtk8250_data *data = arg;
+
+ *val = data->force_pins;
+ return 0;
+}
+
+static int mtk8250_force_pins_set(void *arg, u64 val)
+{
+ struct mtk8250_data *data = arg;
+ int ret = 0;
+
+ data->force_pins = !!val;
+ if (data->force_pins && data->dev) {
+ ret = pinctrl_pm_select_default_state(data->dev);
+ if (ret)
+ dev_warn(data->dev,
+ "pinctrl default state failed: %d\n",
+ ret);
+ }
+
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(mtk8250_force_pins_fops, mtk8250_force_pins_get,
+ mtk8250_force_pins_set, "%llu\n");
+
+DEFINE_SIMPLE_ATTRIBUTE(mtk8250_rx_watch_fops, mtk8250_rx_watch_get,
+ mtk8250_rx_watch_set, "%llu\n");
+
+static int mtk8250_debugfs_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, mtk8250_debugfs_show, inode->i_private);
+}
+
+static const struct file_operations mtk8250_debugfs_fops = {
+ .open = mtk8250_debugfs_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static void mtk8250_debugfs_init(struct platform_device *pdev,
+ struct mtk8250_data *data)
+{
+ struct uart_8250_port *up;
+
+ if (!mtk8250_debugfs_root)
+ mtk8250_debugfs_root = debugfs_create_dir("mtk8250", NULL);
+ if (IS_ERR_OR_NULL(mtk8250_debugfs_root))
+ return;
+
+ data->debugfs_dir = debugfs_create_dir(dev_name(&pdev->dev),
+ mtk8250_debugfs_root);
+ if (IS_ERR_OR_NULL(data->debugfs_dir))
+ return;
+
+ up = serial8250_get_port(data->line);
+ if (!up)
+ return;
+
+ debugfs_create_file("regs", 0400, data->debugfs_dir, up,
+ &mtk8250_debugfs_fops);
+ debugfs_create_file("force_pins", 0600, data->debugfs_dir, data,
+ &mtk8250_force_pins_fops);
+ debugfs_create_file("rx_watch", 0600, data->debugfs_dir, data,
+ &mtk8250_rx_watch_fops);
+}
+
+static void mtk8250_debugfs_exit(struct mtk8250_data *data)
+{
+ debugfs_remove_recursive(data->debugfs_dir);
+ cancel_delayed_work_sync(&data->rx_work);
+ data->rx_watch = false;
+ data->debugfs_dir = NULL;
+}
+#endif
+
static int __maybe_unused mtk8250_runtime_suspend(struct device *dev)
{
struct mtk8250_data *data = dev_get_drvdata(dev);
@@ -543,6 +832,17 @@ static int mtk8250_probe(struct platform
data->clk_count = 0;
+#if IS_ENABLED(CONFIG_DEBUG_FS)
+ if (of_device_is_compatible(pdev->dev.of_node, "mediatek,mt7988-uart")) {
+ data->dev = &pdev->dev;
+ data->gpio_base = devm_ioremap(&pdev->dev, MT7988_GPIO_BASE,
+ MT7988_GPIO_SIZE);
+ data->iocfg_tr_base = devm_ioremap(&pdev->dev,
+ MT7988_IOCFG_TR_BASE,
+ MT7988_IOCFG_TR_SIZE);
+ }
+#endif
+
if (pdev->dev.of_node) {
err = mtk8250_probe_of(pdev, &uart.port, data);
if (err)
@@ -579,11 +879,19 @@ static int mtk8250_probe(struct platform
if (data->line < 0)
return data->line;
+#if IS_ENABLED(CONFIG_DEBUG_FS)
+ INIT_DELAYED_WORK(&data->rx_work, mtk8250_rx_watch_work);
+#endif
+
data->rx_wakeup_irq = platform_get_irq_optional(pdev, 1);
pm_runtime_set_active(&pdev->dev);
pm_runtime_enable(&pdev->dev);
+#if IS_ENABLED(CONFIG_DEBUG_FS)
+ mtk8250_debugfs_init(pdev, data);
+#endif
+
return 0;
}
@@ -591,6 +899,10 @@ static void mtk8250_remove(struct platfo
{
struct mtk8250_data *data = platform_get_drvdata(pdev);
+#if IS_ENABLED(CONFIG_DEBUG_FS)
+ mtk8250_debugfs_exit(data);
+#endif
+
pm_runtime_get_sync(&pdev->dev);
serial8250_unregister_port(data->line);
Note the above patch is for openwrt snapshot.
I am now looking to add a āpinctrl hogā to lock the pins in UART mode so nothing else can remux them.
How do your dts changes look like? I wonder why something should remux the pins (and can if they already bound). As far as i see most code is for the watching and only code in mtk8250_force_pins_set should be neededā¦maybe some supend issue?
yes the mtk8250_force_pins_set is what is needed to have the console showing using uart2. I am changing the dts to have something like this (pinctrl hogging) to check if there is something remuxing the pins. I donāt think i should need something like mtk8250_force_pins_set
+&pio {
+ pinctrl-names = "default";
+ pinctrl-0 = <&uart2_3_pins>;
+
+ i2c1_pins: i2c1-pins {
+ mux {
+ function = "i2c";
+ groups = "i2c1_0";
+ };
+ };
+
+ uart2_3_pins: uart2-3-pins {
+ mux {
+ function = "uart";
+ groups = "uart2_3_lite";
+ };
+ conf-rx {
+ pins = "JTAG_JTDI";
+ input-enable;
+ };
+ };
.......
letās see if it worksā¦not an expert on this so been trial & error but relieved there is no hw issue
That is wrongā¦this have to be in the uart node not pioā¦and why conf-rx? And the mux above for i2c (i guess it is only your unrelated local change)
let me prepare the changes i think needed for this in code and dts so far (except the routing from uart0 to uart2)
i need to remove conf-rx this was part of an earlier test ⦠the &pio change with pinctrl-0 = <&uart2_3_pins> I agree is wrong but want to set it as the default state to check if i get any of the remux issues (without the need to force the pins) ā¦no intention for any of this work to be upstreamed but find the root cause
pushed these change to 6.19-rcā¦only these should be needed for uart1/uart2 to work
basicly the both uart seems workingā¦started a minicom there to debug-uart of my other R4 ![]()
only looks they are swapped (uart0 on header is ttyS2 and uart1 is ttyS1)
[ 0.849373] 11000000.serial: ttyS0 at MMIO 0x11000000 (irq = 99, base_baud = 2500000) is a ST16650V2
[ 0.858662] printk: legacy console [ttyS0] enabled
[ 0.900106] 11000100.serial: ttyS1 at MMIO 0x11000100 (irq = 100, base_baud = 2500000) is a ST16650V2
[ 0.930193] 11000200.serial: ttyS2 at MMIO 0x11000200 (irq = 101, base_baud = 2500000) is a ST16650V2
6,8,10 = ttyS2
9,11,13 = ttyS1
So far so right,only confusing that uart2 on 8/10 is labeled uart0
https://wiki.fw-web.de/doku.php?id=en:bpi-r4:start#gpio
tx and rx are working for both uart
but noticed that something broke xsphy in 6.19, but i guess it is not relatedā¦
[ 1.371212] phy phy-11e10000.xs-phy.2: error -EBUSY: can't request region for resource [mem 0x00000000-0x000003ff]
[ 1.381579] mtk-xsphy 11e10000.xs-phy: failed to remap phy regs
[ 1.395495] mtk-xsphy 11e10000.xs-phy: probe with driver mtk-xsphy failed with error -16
Seems it is caused by my patch try to fix binding errors (looks like i defined xsphy too small),but have seen patch from angelo regarding sameā¦will revert mine and try his oneā¦seems working better.
so that worked - testing on your 6.19-rc repo. I had to change the the stdout-path and include a stdin-path on the dts though, and update the uEnv and point the console to ttyS2. earlycon though doesnāt work (the console gets stuck) this is most likely to u-boot.
Regarding u-boot/mtk-atf, and based on my tests so far with openwrt thereās quite some work, namely
a/plat/mediatek/mt7988/aarch64/plat_helpers.S UART0_BASE needs to move to UART2_BASE on a couple of places
b/plat/mediatek/mt7988/include/mt7988_def.h needs to move from UART0_BASE to UART2_BASE
also need to set GPIO58 and 59 to GPIO mode 3
i will report back if i get the uboot console to work with uart2