From nobody Mon Sep 28 01:13:33 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=none dis=none) header.from=gmail.com ARC-Seal: i=1; a=rsa-sha256; t=1786156489; cv=none; d=zohomail.com; s=zohoarc; b=UVL7xuTzjxdIrxO6srxLEZT/sQfZYEFGSBnEAMfCEOPqkJRkUUA9V/JQXRhdoiVik+i30Wh9fpyotdrvZJ36qEN091qCp+8204rNPhhvmvr+SWfuxxgqCygpW0z7eiFZVRRafS/CAO67mmVXhEUkHxyda6gWBfklo18xF2S4/gI= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1786156489; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=PVzJNrLLxYM7VVuBvSUBASOOzGYTMhMIl9eWG2HLWks=; b=Fo9xlhPcvgiBG3VaRNE4gqc8x7rc75cP2EdF5h0uiUJ79+hvNH4vdx2dEO34WlrttfAUSYq7+zD0YB9AQk/8b4qclVEWn2XvR1qTDNOvk+MiWvDcl7xAueiSJA01/ohhvpjZGYT/BWH2Fk6rsL321KZsIo6YeqH2mjJQPPempoQ= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists1p.gnu.org (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1786156488925557.3813421882886; Fri, 7 Aug 2026 19:34:48 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1wsWt4-0000Cl-OO; Fri, 07 Aug 2026 22:34:15 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists1p.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1wsWsw-0000CM-FG for qemu-devel@nongnu.org; Fri, 07 Aug 2026 22:34:06 -0400 Received: from mail-pj1-x1036.google.com ([2607:f8b0:4864:20::1036]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1wsWsc-00076p-T1 for qemu-devel@nongnu.org; Fri, 07 Aug 2026 22:33:50 -0400 Received: by mail-pj1-x1036.google.com with SMTP id 98e67ed59e1d1-381b831d535so231742a91.0 for ; Fri, 07 Aug 2026 19:33:46 -0700 (PDT) Received: from [127.0.1.1] ([14.19.40.154]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-3925ff69eabsm4475835a91.15.2026.08.07.19.33.41 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 07 Aug 2026 19:33:44 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1786156425; x=1786761225; darn=nongnu.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=PVzJNrLLxYM7VVuBvSUBASOOzGYTMhMIl9eWG2HLWks=; b=bdki36k4MRymFx+AXiDR48lOwdDacOMGV5OSVCSMZfnveUGqjjMPsdcVbRVfhUIYgL ULLTH+0dxdSGfpN9F8QKRTbyFJlsRV0kv1f97QGA4O9w4JQx9yJP0e86PIND3CAQk5Zt Yz3BfJi34bHK2XsFdtd2f3foZr0prn7zd3YW+/AEVIzWsIlyF08wmiSyWoVtugd5AnAD WHfuFtewrrUdLvfQ9dCMEnFvmg4dFp1vcf1Lb7ROPhqcgoLNSuDMzFmc/QZ8/EP2WCUu fahY5mGG7woBCaWNxylzz9S5xPkojhMXfcBHFC3SHuik3KvfRCz4SRtl9LkK/h5652yo uUHQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1786156425; x=1786761225; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to :content-type; bh=PVzJNrLLxYM7VVuBvSUBASOOzGYTMhMIl9eWG2HLWks=; b=de/TdbKi7dk7cuf+3EUk6Hy8s+Su+bC/Bf3BDEFpJ+5tgfiWKYjwKtcDXbtJFyLxs8 +Cu7VBtlZl0XsgqMCSVuaDUwIlOlvL3iGHsaS1tEoDCXHO0dpYYb0HM3poYZ4YaCNmhS MXY6Akq/XOB6yRM+BXkZ8KCorsro0xU1meBsTj9zxyh7q5t/K2t923TOOY6MjF1/NPkA rwG0FT/OINf8WXD4s5Zi4IFDpC2RL1cVXg3/x5IZYPtjOB3G6VyGV9YYdbgylPQBxK+Z w3UowriqgIvKvgh2q2sBvglFFkcM8aVEpO4WcnU3GeehuhWZ+9OLLNF/KXgB4vQBm2Hl tiUw== X-Gm-Message-State: AOJu0YyA6YTmYme6usL+Yzzx8j3H7IeMo9ZLLHVLttOSs8pJuNOOPdgR cbMH2ne8C28tWALoCB7tWqnjpHGcSIr0lnLhVc5RJvQqO4dMi2y554Vq X-Gm-Gg: AR+sD10xUe+fgTQ1q3h/CbREhbXSd/yNTrX+K69e9R/P2NOsh+099d+M3KwfO89Wg24 3JPjGPt1D14JqXEQ7h7wfqEuk5hQNofjfxHJL/SU76C5lWqAnJ+/HTyVv5rdH2t0k1iZkgbNvnE ieQk2v56/jkr1oRuEEDQNrTE7b2CNuO0nw8Yu/pvP6pmBKeM6lC8BO3utOmh/T1By6YH/ObTU9W FXC41QVQui0lUiJBKJDUslcBiURuHaMdEPnIEQBLPW8QtpVO6izQX6OjWVRU+0jTTTnh+edUUul lVoI29M9uzDX7YMWkacmkIn+JkP2tPmFeCaoXY0uveDCc5J+uq2luaecrh29NvRbIp1hb6ZBMZB 9O1ufGdZ7BGvtzlVJd5gvuIdDVsHEbWBgiMNKieINANpP1TrL1aciuWP2vBW7TJZaozjDfe/BfM cBCJ548VJQrkKJLf1Ney63xV/uzjBgPAkuk8i76QzKR+6hHR/wca93iTWjAis= X-Received: by 2002:a17:90b:38d2:b0:38e:2f21:3bb5 with SMTP id 98e67ed59e1d1-3926237cd78mr8931032a91.21.1786156425149; Fri, 07 Aug 2026 19:33:45 -0700 (PDT) From: WX Chen Date: Sat, 08 Aug 2026 10:32:42 +0800 Subject: [PATCH v3 1/3] hw/char: add K230 DW 8250-compatible UART MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260808-feat-k230-uart-v3-v3-1-d73b31f0034b@gmail.com> References: <20260808-feat-k230-uart-v3-v3-0-d73b31f0034b@gmail.com> In-Reply-To: <20260808-feat-k230-uart-v3-v3-0-d73b31f0034b@gmail.com> To: qemu-devel@nongnu.org Cc: Paolo Bonzini , =?utf-8?q?Marc-Andr=C3=A9_Lureau?= , Chao Liu , Palmer Dabbelt , Alistair Francis , Weiwei Li , Daniel Henrique Barboza , Liu Zhiwei , qemu-riscv@nongnu.org, Fabiano Rosas , Laurent Vivier , WX Chen X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=ed25519-sha256; t=1786156416; l=36542; i=wxchen0913@gmail.com; s=20260725; h=from:subject:message-id; bh=xtUWI0tMlm9KQNVgH+vp0EIw01/kVdiHf5JkdlpgMOw=; b=q4Zo3KYtoh6GYWnUrv65u0z4UTwUCojmzrgHeFUy+TkGTPZkPLxywtCMrJ+wJA88WtjaHQtkV LBdzhgaqE7NCtLOqLXFLaFR0xTz6l7lMJsJIE6v4FlGaYN8nWWaV0To X-Developer-Key: i=wxchen0913@gmail.com; a=ed25519; pk=FX63IRxmtz1BCNzHxOOWDPTo8L/D0YUxlIKc1WKi7wo= Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists1p.gnu.org; Received-SPF: pass client-ip=2607:f8b0:4864:20::1036; envelope-from=wxchen0913@gmail.com; helo=mail-pj1-x1036.google.com X-Spam_score_int: -17 X-Spam_score: -1.8 X-Spam_bar: - X-Spam_report: (-1.8 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FREEMAIL_ENVFROM_END_DIGIT=0.25, FREEMAIL_FROM=0.001, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=unavailable autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @gmail.com) X-ZM-MESSAGEID: 1786156492070158500 Implement a K230 SoC DesignWare 8250-compatible UART controller model for QEMU, capable of running the Linux 8250_dw driver and providing an interactive shell. Implemented: - Standard 16550 registers (RBR/THR/DLL, IER/DLH, IIR/FCR, LCR, MCR, LSR, MSR, SCR) and DesignWare-specific registers (USR, TFL, RFL, SRR, SRTS, SBCR, SDMAM, SFE, SRT, STET, HTX, CPR, UCV, CTR) - DLAB switching; 32-byte TX/RX FIFO; synchronous transmit with backpressure handling - Loopback mode; chardev BREAK routed to LSR.BI/FE via CHR_EVENT_BREAK - Four-level prioritized interrupt scheme (ELSI / RX Data / Timeout / THRE), with edge-triggered THRE - RX Character Timeout interrupt (IID=3D0xc) with 4-char-time timer that tracks the programmed divisor latch - Busy Detect interrupt (IID=3D0x7): LCR (and its shadow SBCR) writes while USR.BUSY=3D1 are rejected and raise IID=3D0x7, cleared by reading USR; USR.BUSY reflects TX-not-empty / RX-data-ready - Shadow registers alias their underlying fields - SRR (UR/RFR/XFR) self-clearing software reset Not implemented: - RS485 transceiver control (TCR/DE_EN/RE_EN/DET/TAT) - 9-bit multidrop (LCR_EXT/RAR/TAR) - Fractional baud rate (DLF) - Auto Flow Control (MCR.AFCE) - IrDA SIR mode (MCR.SIRE) - Low-power divisor latch (LPDLL/LPDLH) - FIFO access test mode (FAR/TFR/RFW) - DMA Software Acknowledge (DMASA) Signed-off-by: WX Chen Reviewed-by: Daniel Henrique Barboza --- hw/char/Kconfig | 3 + hw/char/k230_uart.c | 816 ++++++++++++++++++++++++++++++++++++++++= ++++ hw/char/meson.build | 1 + include/hw/char/k230_uart.h | 198 +++++++++++ 4 files changed, 1018 insertions(+) diff --git a/hw/char/Kconfig b/hw/char/Kconfig index 020c0a84bb6e5f11cc7d532a7999ade23505a1ba..bd000f7127e7881dabac191c81b= 2bcf66f549250 100644 --- a/hw/char/Kconfig +++ b/hw/char/Kconfig @@ -91,6 +91,9 @@ config GOLDFISH_TTY config SHAKTI_UART bool =20 +config K230_UART + bool + config IP_OCTAL_232 bool default y diff --git a/hw/char/k230_uart.c b/hw/char/k230_uart.c new file mode 100644 index 0000000000000000000000000000000000000000..7d83a04aaf3129f2908ea97adb8= 05cfca9f13095 --- /dev/null +++ b/hw/char/k230_uart.c @@ -0,0 +1,816 @@ +/* + * K230 UART + * + * Copyright (c) 2026 WX Chen + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "hw/char/k230_uart.h" +#include "hw/core/irq.h" +#include "hw/core/qdev-properties-system.h" +#include "migration/vmstate.h" +#include "qemu/module.h" +#include "qemu/timer.h" +#include "qemu/host-utils.h" +#include "system/memory.h" + +/* Serial clock from the K230 DT "clock-frequency" property (50 MHz). */ +#define K230_UART_SCLK_HZ 50000000ull + +static void k230_uart_xmit(K230UartState *s); +static void k230_uart_update_all(K230UartState *s); +static uint64_t k230_uart_read_rbr(K230UartState *s); +static void k230_uart_push_rx_byte(K230UartState *s, uint8_t ch); +static void k230_uart_reset_hold(Object *obj, ResetType type); + +/* ---- interrupt line ---- */ + +/* + * RX FIFO trigger level. When rx_count reaches this, + * the RX Data Available interrupt (IID=3D0x4) fires. + */ +static uint32_t k230_uart_rx_trigger_level(const K230UartState *s) +{ + if (!FIELD_EX32(s->fcr, FCR, FIFOE)) { + return 1; /* non-FIFO mode: 1 byte */ + } + switch (FIELD_EX32(s->fcr, FCR, RT)) { + case 0: return 1; + case 1: return K230_UART_FIFO_DEPTH / 4; + case 2: return K230_UART_FIFO_DEPTH / 2; + case 3: return K230_UART_FIFO_DEPTH - 2; + default: return 1; + } +} + +static bool k230_uart_rx_line_status_int(const K230UartState *s) +{ + return FIELD_EX32(s->ier, IER, ELSI) && + (FIELD_EX32(s->lsr, LSR, OE) || FIELD_EX32(s->lsr, LSR, PE) || + FIELD_EX32(s->lsr, LSR, FE) || FIELD_EX32(s->lsr, LSR, BI)); +} + +static void k230_uart_update_irq(K230UartState *s) +{ + bool irq =3D false; + uint32_t rx_itl =3D k230_uart_rx_trigger_level(s); + + /* RX trigger level > 1 */ + if (FIELD_EX32(s->ier, IER, ERBFI) && + (s->rx_count >=3D rx_itl || s->timeout_ipending)) { + irq =3D true; + } + /* Transmit Holding Register Empty Interrupt */ + if (FIELD_EX32(s->ier, IER, ETBEI) && s->thr_ipending) { + irq =3D true; + } + /* Receiver Line Status Interrupt */ + if (k230_uart_rx_line_status_int(s)) { + irq =3D true; + } + /* Busy detect interrupt (DW-specific, no IER gate). */ + if (s->busy_ipending) { + irq =3D true; + } + qemu_set_irq(s->irq, irq ? 1 : 0); +} + +/* ---- FIFO reset ---- */ + +static void k230_uart_fifo_reset(K230UartState *s, bool rx, bool tx) +{ + if (rx) { + s->rx_head =3D s->rx_tail =3D s->rx_count =3D 0; + s->lsr =3D FIELD_DP32(s->lsr, LSR, DR, 0); + s->timeout_ipending =3D 0; + timer_del(&s->rx_timeout); + } + if (tx) { + s->tx_head =3D s->tx_tail =3D s->tx_count =3D 0; + s->lsr =3D FIELD_DP32(s->lsr, LSR, THRE, 1); + s->lsr =3D FIELD_DP32(s->lsr, LSR, TEMT, 1); + s->thr_ipending =3D 1; + } + k230_uart_update_all(s); +} + +/* ---- transmit ---- */ + +static gboolean k230_uart_chr_can_write(void *do_not_use, GIOCondition con= d, + void *opaque) +{ + K230UartState *s =3D K230_UART(opaque); + if (!s->htx) { + k230_uart_xmit(s); + } + return G_SOURCE_REMOVE; +} + +/* ---- baud / char-transmit time ---- */ + +/* + * Recompute the per-character transmit time from the current divisor latc= h: + * + * baud =3D sclk / (16 * divisor) + * char_ns =3D 10 * 16 * divisor * 1e9 / sclk (10 =3D 8N1 start+data= +stop) + * + * Drives the RX timeout interrupt (IID=3D0xc), which fires after 4 char t= imes. + * divisor =3D=3D 0 means "unprogrammed": keep the previous (reset default= ) value. + */ +static void k230_uart_update_char_time(K230UartState *s) +{ + uint32_t divisor =3D ((uint32_t)s->dlh << 8) | s->dll; + if (divisor =3D=3D 0) { + return; + } + s->char_transmit_time =3D muldiv64(10ull * 16 * divisor, + NANOSECONDS_PER_SECOND, + K230_UART_SCLK_HZ); +} + +/* Send all characters in THR or TX-FIFO into char backend */ +static void k230_uart_xmit(K230UartState *s) +{ + int ret; + + if (s->tx_count =3D=3D 0 || s->htx) { + return; + } + + while (s->tx_count > 0) { + uint8_t ch =3D (uint8_t)s->tx_fifo[s->tx_tail]; + + if (FIELD_EX32(s->mcr, MCR, LOOPBACK)) { + /* Go to RX fifo */ + k230_uart_push_rx_byte(s, ch); + ret =3D 1; + } else { + /* Go to char backend */ + ret =3D qemu_chr_fe_write(&s->chr, &ch, 1); + } + if (ret < 0) { + /* Wait for backend to be accessible then call the callback */ + qemu_chr_fe_add_watch(&s->chr, G_IO_OUT, + k230_uart_chr_can_write, s); + break; + } + + s->tx_tail =3D (s->tx_tail + 1) % K230_UART_FIFO_DEPTH; + s->tx_count--; + } + + s->lsr =3D FIELD_DP32(s->lsr, LSR, TEMT, (s->tx_count =3D=3D 0) ? 1 : = 0); + k230_uart_update_all(s); +} + +/* ---- RX timeout ---- */ + +/* Character timeout callback */ +static void k230_uart_rx_timeout(void *opaque) +{ + K230UartState *s =3D K230_UART(opaque); + if (s->rx_count > 0) { + s->timeout_ipending =3D 1; + k230_uart_update_all(s); + } +} + +/* ---- receive ---- */ + +/* Push a byte to rxfifo(when FIFO_EN is set) or RBR(FIFO_EN not set). */ +static void k230_uart_push_rx_byte(K230UartState *s, uint8_t ch) +{ + bool fifo_en =3D FIELD_EX32(s->fcr, FCR, FIFOE); + uint16_t capacity =3D fifo_en ? K230_UART_FIFO_DEPTH : 1; + + if (s->rx_count < capacity) { + s->rx_fifo[s->rx_head] =3D ch; + s->rx_head =3D (s->rx_head + 1) % K230_UART_FIFO_DEPTH; + s->rx_count++; + s->lsr =3D FIELD_DP32(s->lsr, LSR, DR, 1); + timer_mod(&s->rx_timeout, + qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + + 4 * s->char_transmit_time); + } else { + s->lsr =3D FIELD_DP32(s->lsr, LSR, OE, 1); + } + k230_uart_update_all(s); +} + +/* ---- derived register recomputation ---- */ + +static void k230_uart_update_thre(K230UartState *s) +{ + bool should_set =3D (s->tx_count =3D=3D 0); + if (FIELD_EX32(s->ier, IER, PTIME) + && FIELD_EX32(s->fcr, FCR, FIFOE)) { + uint8_t tet =3D FIELD_EX32(s->fcr, FCR, TET); + uint16_t threshold; + switch (tet) { + case 0x0: + threshold =3D 0; + break; + case 0x1: + threshold =3D 2; + break; + case 0x2: + threshold =3D K230_UART_FIFO_DEPTH / 4; + break; + case 0x3: + threshold =3D K230_UART_FIFO_DEPTH / 2; + break; + default: + threshold =3D 0; + break; + } + should_set =3D (s->tx_count <=3D threshold); + } + + uint8_t old_thre =3D FIELD_EX32(s->lsr, LSR, THRE); + s->lsr =3D FIELD_DP32(s->lsr, LSR, THRE, should_set ? 1 : 0); + + /* Edge trigger */ + if (should_set && !old_thre) { + s->thr_ipending =3D 1; + } +} + +static bool k230_uart_is_busy(const K230UartState *s) +{ + return !FIELD_EX32(s->lsr, LSR, TEMT) || FIELD_EX32(s->lsr, LSR, DR); +} + +static void k230_uart_update_usr(K230UartState *s) +{ + bool fifo_en =3D FIELD_EX32(s->fcr, FCR, FIFOE); + uint16_t capacity =3D fifo_en ? K230_UART_FIFO_DEPTH : 1; + + s->usr =3D 0; + if (k230_uart_is_busy(s)) { + s->usr =3D FIELD_DP32(s->usr, USR, BUSY, 1); + } + s->usr =3D FIELD_DP32(s->usr, USR, TFNF, s->tx_count < capacity ? 1 : = 0); + s->usr =3D FIELD_DP32(s->usr, USR, TFE, s->tx_count =3D=3D 0 ? 1 : 0); + s->usr =3D FIELD_DP32(s->usr, USR, RFNE, s->rx_count !=3D 0 ? 1 : 0); + s->usr =3D FIELD_DP32(s->usr, USR, RFF, s->rx_count =3D=3D capacity ? = 1 : 0); +} + +static void k230_uart_update_iir(K230UartState *s) +{ + s->iir =3D 0; + /* Indicate whether FIFO is enabled or not */ + if (FIELD_EX32(s->fcr, FCR, FIFOE)) { + s->iir =3D FIELD_DP32(s->iir, IIR, FIFOSE, 0x3); + } + + if (k230_uart_rx_line_status_int(s)) { + /* Receiver Line Status Interrupt */ + s->iir =3D FIELD_DP32(s->iir, IIR, IID, 0x6); + } else if (FIELD_EX32(s->ier, IER, ERBFI) && + (s->rx_count >=3D k230_uart_rx_trigger_level(s))) { + /* Receiver Data Available Interrupt */ + s->iir =3D FIELD_DP32(s->iir, IIR, IID, 0x4); + } else if (FIELD_EX32(s->ier, IER, ERBFI) && s->timeout_ipending) { + /* Character timeout interrupt */ + s->iir =3D FIELD_DP32(s->iir, IIR, IID, 0xc); + } else if (FIELD_EX32(s->ier, IER, ETBEI) && s->thr_ipending) { + /* THR Empty Interrupt */ + s->iir =3D FIELD_DP32(s->iir, IIR, IID, 0x2); + } else if (s->busy_ipending) { + /* Busy detect: LCR written while USR.BUSY=3D1 (DW-specific) */ + s->iir =3D FIELD_DP32(s->iir, IIR, IID, 0x7); + } else { + s->iir =3D FIELD_DP32(s->iir, IIR, IID, 0x1); + } +} + + +static void k230_uart_update_msr(K230UartState *s) +{ + uint8_t old_status =3D s->msr & 0xf0; + uint8_t new_status; + + if (FIELD_EX32(s->mcr, MCR, LOOPBACK)) { + /* + * Standard 16550 loopback mapping: + * DTR -> DSR, RTS -> CTS, OUT1 -> RI, OUT2 -> DCD + */ + new_status =3D 0; + if (FIELD_EX32(s->mcr, MCR, RTS)) { + new_status |=3D 0x10; /* CTS */ + } + if (FIELD_EX32(s->mcr, MCR, DTR)) { + new_status |=3D 0x20; /* DSR */ + } + if (FIELD_EX32(s->mcr, MCR, OUT1)) { + new_status |=3D 0x40; /* RI */ + } + if (FIELD_EX32(s->mcr, MCR, OUT2)) { + new_status |=3D 0x80; /* DCD */ + } + } else { + /* No real modem: report CTS/DSR/DCD as active (ready) */ + new_status =3D 0xb0; /* CTS | DSR | DCD */ + } + + /* Accumulate delta bits on status changes */ + uint8_t deltas =3D s->msr & 0x0f; + uint8_t changes =3D old_status ^ new_status; + if (changes & 0x10) { + deltas |=3D 0x01; /* DCTS */ + } + if (changes & 0x20) { + deltas |=3D 0x02; /* DDSR */ + } + if ((old_status & 0x40) && !(new_status & 0x40)) { + deltas |=3D 0x04; /* TERI */ + } + if (changes & 0x80) { + deltas |=3D 0x08; /* DDCD */ + } + + s->msr =3D new_status | deltas; +} + +static void k230_uart_update_all(K230UartState *s) +{ + k230_uart_update_thre(s); + k230_uart_update_usr(s); + k230_uart_update_iir(s); + k230_uart_update_irq(s); +} + +/* ---- MMIO read ---- */ + +static uint64_t k230_uart_read_rbr(K230UartState *s) +{ + uint64_t ret =3D 0; + + if (s->rx_count > 0) { + ret =3D s->rx_fifo[s->rx_tail]; + s->rx_tail =3D (s->rx_tail + 1) % K230_UART_FIFO_DEPTH; + s->rx_count--; + s->timeout_ipending =3D 0; + if (s->rx_count =3D=3D 0) { + s->lsr =3D FIELD_DP32(s->lsr, LSR, DR, 0); + timer_del(&s->rx_timeout); + } else { + timer_mod(&s->rx_timeout, + qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + + 4 * s->char_transmit_time); + } + qemu_chr_fe_accept_input(&s->chr); + } + + if (!FIELD_EX32(s->ier, IER, ELCOLR)) { + s->lsr =3D FIELD_DP32(s->lsr, LSR, OE, 0); + s->lsr =3D FIELD_DP32(s->lsr, LSR, PE, 0); + s->lsr =3D FIELD_DP32(s->lsr, LSR, FE, 0); + s->lsr =3D FIELD_DP32(s->lsr, LSR, BI, 0); + } + + k230_uart_update_all(s); + return ret; +} + +static uint64_t k230_uart_read(void *opaque, hwaddr addr, unsigned int siz= e) +{ + K230UartState *s =3D K230_UART(opaque); + uint64_t ret =3D 0; + + switch (addr >> 2) { + case R_RBR_DLL_THR: + if (FIELD_EX32(s->lcr, LCR, DLAB)) { + /* DLL accessible only when not busy. */ + ret =3D !k230_uart_is_busy(s) ? s->dll : 0; + } else { + ret =3D k230_uart_read_rbr(s); + } + break; + case R_IER_DLH: + if (FIELD_EX32(s->lcr, LCR, DLAB)) { + /* DLH accessible only when not busy. */ + ret =3D !k230_uart_is_busy(s) ? s->dlh : 0; + } else { + /* IER */ + ret =3D s->ier; + } + break; + case R_IIR: + ret =3D s->iir; + if (FIELD_EX32(s->iir, IIR, IID) =3D=3D 0x2) { + s->thr_ipending =3D 0; + k230_uart_update_all(s); + } + break; + case R_LCR: + ret =3D s->lcr; + break; + case R_LSR: + ret =3D s->lsr; + /* Clear OE PE FE and Break Interrupt status bits */ + s->lsr =3D FIELD_DP32(s->lsr, LSR, OE, 0); + s->lsr =3D FIELD_DP32(s->lsr, LSR, PE, 0); + s->lsr =3D FIELD_DP32(s->lsr, LSR, FE, 0); + s->lsr =3D FIELD_DP32(s->lsr, LSR, BI, 0); + k230_uart_update_all(s); + break; + case R_MCR: + ret =3D s->mcr; + break; + case R_MSR: + ret =3D s->msr; + s->msr &=3D 0xf0; /* clear delta bits on read */ + break; + case R_SCR: + ret =3D s->scr; + break; + + /* Shadow registers: aliases of standard registers. */ + case R_SRTS: + ret =3D FIELD_EX32(s->mcr, MCR, RTS); + break; + case R_SBCR: + ret =3D FIELD_EX32(s->lcr, LCR, BC); + break; + case R_SDMAM: + ret =3D FIELD_EX32(s->fcr, FCR, DMAM); + break; + case R_SFE: + ret =3D FIELD_EX32(s->fcr, FCR, FIFOE); + break; + case R_SRT: + ret =3D FIELD_EX32(s->fcr, FCR, RT); + break; + case R_STET: + ret =3D FIELD_EX32(s->fcr, FCR, TET); + break; + case R_HTX: + ret =3D s->htx; + break; + + case R_CPR: + ret =3D K230_UART_R_CPR; + break; + case R_UCV: + ret =3D K230_UART_R_UCV; + break; + case R_DLF: + ret =3D 0; + break; + case R_USR: + ret =3D s->usr; + /* Reading USR clears the busy-detect interrupt. */ + if (s->busy_ipending) { + s->busy_ipending =3D 0; + k230_uart_update_all(s); + } + break; + case R_TFL: + /* Transmit FIFO Level: number of bytes in TX FIFO. */ + ret =3D s->tx_count; + break; + case R_RFL: + /* Receive FIFO Level: number of bytes in RX FIFO. */ + ret =3D s->rx_count; + break; + case R_CTR: + /* Component Type Register: fixed ID. */ + ret =3D K230_UART_CTR_VALUE; + break; + default: + ret =3D 0; + break; + } + return ret; +} + +/* ---- MMIO write ---- */ + +static void k230_uart_write(void *opaque, hwaddr addr, + uint64_t val64, unsigned int size) +{ + K230UartState *s =3D K230_UART(opaque); + uint32_t val =3D (uint32_t)val64; + uint32_t offset =3D addr >> 2; + + switch (offset) { + case R_RBR_DLL_THR: + if (FIELD_EX32(s->lcr, LCR, DLAB)) { + /* DLL writable only when not busy. */ + if (!k230_uart_is_busy(s)) { + s->dll =3D val & 0xff; + k230_uart_update_char_time(s); + } + } else { + bool fifo_en =3D FIELD_EX32(s->fcr, FCR, FIFOE); + uint16_t capacity =3D fifo_en ? K230_UART_FIFO_DEPTH : 1; + if (s->tx_count < capacity) { + s->tx_fifo[s->tx_head] =3D val & 0xff; + s->tx_head =3D (s->tx_head + 1) % K230_UART_FIFO_DEPTH; + s->tx_count++; + } else if (!fifo_en) { + s->tx_fifo[s->tx_tail] =3D val & 0xff; + } + s->lsr =3D FIELD_DP32(s->lsr, LSR, THRE, 0); + s->thr_ipending =3D 0; + s->lsr =3D FIELD_DP32(s->lsr, LSR, TEMT, 0); + k230_uart_xmit(s); + } + break; + case R_IER_DLH: + if (FIELD_EX32(s->lcr, LCR, DLAB)) { + /* DLH writable only when not busy. */ + if (!k230_uart_is_busy(s)) { + s->dlh =3D val & 0xff; + k230_uart_update_char_time(s); + } + } else { + uint8_t old_etbei =3D FIELD_EX32(s->ier, IER, ETBEI); + s->ier =3D FIELD_DP32(s->ier, IER, ERBFI, + FIELD_EX32(val, IER, ERBFI)); + s->ier =3D FIELD_DP32(s->ier, IER, ETBEI, + FIELD_EX32(val, IER, ETBEI)); + s->ier =3D FIELD_DP32(s->ier, IER, ELSI, + FIELD_EX32(val, IER, ELSI)); + s->ier =3D FIELD_DP32(s->ier, IER, EDSSI, + FIELD_EX32(val, IER, EDSSI)); + s->ier =3D FIELD_DP32(s->ier, IER, ELCOLR, + FIELD_EX32(val, IER, ELCOLR)); + s->ier =3D FIELD_DP32(s->ier, IER, PTIME, + FIELD_EX32(val, IER, PTIME)); + if (!old_etbei && FIELD_EX32(s->ier, IER, ETBEI) + && FIELD_EX32(s->lsr, LSR, THRE)) { + s->thr_ipending =3D 1; + } + } + break; + case R_FCR: + if ((val ^ s->fcr) & R_FCR_FIFOE_MASK) { + val |=3D R_FCR_RFIFOR_MASK | R_FCR_XFIFOR_MASK; + } + s->fcr =3D FIELD_DP32(s->fcr, FCR, FIFOE, + FIELD_EX32(val, FCR, FIFOE)); + s->fcr =3D FIELD_DP32(s->fcr, FCR, DMAM, + FIELD_EX32(val, FCR, DMAM)); + s->fcr =3D FIELD_DP32(s->fcr, FCR, RT, + FIELD_EX32(val, FCR, RT)); + s->fcr =3D FIELD_DP32(s->fcr, FCR, TET, + FIELD_EX32(val, FCR, TET)); + if (FIELD_EX32(val, FCR, RFIFOR)) { + k230_uart_fifo_reset(s, true, false); + } + if (FIELD_EX32(val, FCR, XFIFOR)) { + k230_uart_fifo_reset(s, false, true); + } + break; + case R_LCR: + /* + * LCR is writable only when USR.BUSY =3D=3D 0. A write while bus= y is + * ignored and triggers the busy-detect interrupt (IID=3D0x7), whi= ch is + * cleared by reading USR. The 8250_dw driver detects this via + * dw8250_check_lcr() and retries after draining the FIFOs. + */ + if (k230_uart_is_busy(s)) { + s->busy_ipending =3D 1; + } else { + s->lcr =3D val & 0xff; + } + break; + case R_MCR: + /* + * K230 advertises AFCE_MODE=3D0 and SIR_MODE=3D0 in CPR, so MCR[5= :6] + * (AFCE/SIRE) are read-only 0. MCR[7] is reserved and also 0. + */ + s->mcr =3D val & 0x1f; + k230_uart_update_msr(s); + break; + case R_SCR: + s->scr =3D val & 0xff; + break; + + /* + * Shadow registers: aliases of standard register fields. + * They let software update a single bit without read-modify-write on = the + * original register. SBCR shadows LCR[6] (BC), which is gated by the + * busy-detect logic just like a direct LCR write. + */ + case R_SRTS: + s->mcr =3D FIELD_DP32(s->mcr, MCR, RTS, val & 0x1); + k230_uart_update_msr(s); + break; + case R_SBCR: + if (k230_uart_is_busy(s)) { + s->busy_ipending =3D 1; + } else { + s->lcr =3D FIELD_DP32(s->lcr, LCR, BC, val & 0x1); + } + break; + case R_SDMAM: + s->fcr =3D FIELD_DP32(s->fcr, FCR, DMAM, val & 0x1); + break; + case R_SFE: + /* Shadow of FCR[0]; changing FIFOE resets both FIFOs */ + if ((s->fcr ^ val) & R_FCR_FIFOE_MASK) { + k230_uart_fifo_reset(s, true, true); + } + s->fcr =3D FIELD_DP32(s->fcr, FCR, FIFOE, val & 0x1); + break; + case R_SRT: + s->fcr =3D FIELD_DP32(s->fcr, FCR, RT, val & 0x3); + break; + case R_STET: + s->fcr =3D FIELD_DP32(s->fcr, FCR, TET, val & 0x3); + break; + case R_HTX: + s->htx =3D val & 0x1; + /* Clearing HTX resumes transmission of any buffered TX data. */ + if (!s->htx) { + k230_uart_xmit(s); + } + break; + + case R_SRR: + if (FIELD_EX32(val, SRR, UR)) { + device_cold_reset(DEVICE(s)); + return; + } + if (FIELD_EX32(val, SRR, RFR)) { + k230_uart_fifo_reset(s, true, false); + } + if (FIELD_EX32(val, SRR, XFR)) { + k230_uart_fifo_reset(s, false, true); + } + break; + + default: + break; + } + k230_uart_update_all(s); +} + +/* ---- device lifecycle ---- */ + +static const MemoryRegionOps k230_uart_ops =3D { + .read =3D k230_uart_read, + .write =3D k230_uart_write, + .endianness =3D DEVICE_LITTLE_ENDIAN, + .impl.min_access_size =3D 4, + .impl.max_access_size =3D 4, +}; + +static void k230_uart_init(Object *obj) +{ + K230UartState *s =3D K230_UART(obj); + SysBusDevice *sbd =3D SYS_BUS_DEVICE(obj); + + memory_region_init_io(&s->mmio, OBJECT(s), &k230_uart_ops, s, + TYPE_K230_UART, 0x100); + sysbus_init_mmio(sbd, &s->mmio); + sysbus_init_irq(sbd, &s->irq); +} + +static int k230_uart_can_receive(void *opaque) +{ + K230UartState *s =3D K230_UART(opaque); + return K230_UART_FIFO_DEPTH - s->rx_count; +} + +static void k230_uart_receive(void *opaque, const uint8_t *buf, int size) +{ + K230UartState *s =3D K230_UART(opaque); + for (int i =3D 0; i < size; i++) { + k230_uart_push_rx_byte(s, buf[i]); + } +} + +static void k230_uart_event(void *opaque, QEMUChrEvent event) +{ + K230UartState *s =3D K230_UART(opaque); + + if (event =3D=3D CHR_EVENT_BREAK) { + k230_uart_push_rx_byte(s, 0); + s->lsr =3D FIELD_DP32(s->lsr, LSR, BI, 1); + s->lsr =3D FIELD_DP32(s->lsr, LSR, FE, 1); + k230_uart_update_all(s); + } +} + +static void k230_uart_reset_hold(Object *obj, ResetType type) +{ + K230UartState *s =3D K230_UART(obj); + s->lcr =3D 0; + s->fcr =3D 0; + s->ier =3D 0; + s->mcr =3D 0; + s->scr =3D 0; + s->dll =3D 0; + s->dlh =3D 0; + s->lsr =3D 0x60; + s->msr =3D 0xb0; + + s->rx_head =3D s->rx_tail =3D s->rx_count =3D 0; + s->tx_head =3D s->tx_tail =3D s->tx_count =3D 0; + s->htx =3D 0; + + qemu_set_irq(s->irq, 0); + + s->thr_ipending =3D 0; + s->timeout_ipending =3D 0; + s->busy_ipending =3D 0; + timer_del(&s->rx_timeout); + /* Default to 115200 baud until the driver programs the divisor latch.= */ + s->char_transmit_time =3D (NANOSECONDS_PER_SECOND / 115200) * 10; + + k230_uart_update_all(s); +} + +static void k230_uart_realize(DeviceState *dev, Error **errp) +{ + K230UartState *s =3D K230_UART(dev); + qemu_chr_fe_set_handlers(&s->chr, k230_uart_can_receive, + k230_uart_receive, k230_uart_event, + NULL, s, NULL, true); + + timer_init_ns(&s->rx_timeout, QEMU_CLOCK_VIRTUAL, + k230_uart_rx_timeout, s); +} + +static int k230_uart_post_load(void *opaque, int version_id) +{ + K230UartState *s =3D K230_UART(opaque); + + /* + * iir and usr are derived from the saved state; recompute them after + * migration so the device is consistent. + */ + k230_uart_update_all(s); + return 0; +} + +static const VMStateDescription vmstate_k230_uart =3D { + .name =3D "k230.uart", + .version_id =3D 2, + .minimum_version_id =3D 1, + .post_load =3D k230_uart_post_load, + .fields =3D (const VMStateField[]) { + VMSTATE_UINT8(lcr, K230UartState), + VMSTATE_UINT8(fcr, K230UartState), + VMSTATE_UINT8(ier, K230UartState), + VMSTATE_UINT8(dll, K230UartState), + VMSTATE_UINT8(dlh, K230UartState), + VMSTATE_UINT8(mcr, K230UartState), + VMSTATE_UINT8(lsr, K230UartState), + VMSTATE_UINT8(msr, K230UartState), + VMSTATE_UINT8(scr, K230UartState), + VMSTATE_UINT8(htx, K230UartState), + VMSTATE_UINT16_ARRAY(rx_fifo, K230UartState, + K230_UART_FIFO_DEPTH), + VMSTATE_UINT16_ARRAY(tx_fifo, K230UartState, + K230_UART_FIFO_DEPTH), + VMSTATE_UINT32(rx_head, K230UartState), + VMSTATE_UINT32(rx_tail, K230UartState), + VMSTATE_UINT32(rx_count, K230UartState), + VMSTATE_UINT32(tx_head, K230UartState), + VMSTATE_UINT32(tx_tail, K230UartState), + VMSTATE_UINT32(tx_count, K230UartState), + VMSTATE_UINT8(thr_ipending, K230UartState), + VMSTATE_UINT8(timeout_ipending, K230UartState), + VMSTATE_UINT8(busy_ipending, K230UartState), + VMSTATE_UINT64(char_transmit_time, K230UartState), + VMSTATE_END_OF_LIST() + } +}; + +static const Property k230_uart_properties[] =3D { + DEFINE_PROP_CHR("chardev", K230UartState, chr), +}; + +static void k230_uart_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc =3D DEVICE_CLASS(klass); + ResettableClass *rc =3D RESETTABLE_CLASS(klass); + + dc->realize =3D k230_uart_realize; + rc->phases.hold =3D k230_uart_reset_hold; + dc->vmsd =3D &vmstate_k230_uart; + dc->desc =3D "K230 UART (16550-compatible)"; + device_class_set_props(dc, k230_uart_properties); +} + +static const TypeInfo k230_uart_info =3D { + .name =3D TYPE_K230_UART, + .parent =3D TYPE_SYS_BUS_DEVICE, + .instance_size =3D sizeof(K230UartState), + .instance_init =3D k230_uart_init, + .class_init =3D k230_uart_class_init +}; + +static void k230_uart_register_types(void) +{ + type_register_static(&k230_uart_info); +} + +type_init(k230_uart_register_types) diff --git a/hw/char/meson.build b/hw/char/meson.build index fc3d7ee506fcf8eb1219f6af9689fd90573861c9..23d8ede3f031f80d1b519bf49be= ac9d23502e2cb 100644 --- a/hw/char/meson.build +++ b/hw/char/meson.build @@ -38,6 +38,7 @@ system_ss.add(when: 'CONFIG_STM32L4X5_USART', if_true: fi= les('stm32l4x5_usart.c' system_ss.add(when: 'CONFIG_MCHP_PFSOC_MMUART', if_true: files('mchp_pfsoc= _mmuart.c')) system_ss.add(when: 'CONFIG_HTIF', if_true: files('riscv_htif.c')) system_ss.add(when: 'CONFIG_GOLDFISH_TTY', if_true: files('goldfish_tty.c'= )) +system_ss.add(when: 'CONFIG_K230', if_true: files('k230_uart.c')) =20 specific_ss.add(when: 'CONFIG_TERMINAL3270', if_true: files('terminal3270.= c')) specific_ss.add(when: 'CONFIG_PSERIES', if_true: files('spapr_vty.c')) diff --git a/include/hw/char/k230_uart.h b/include/hw/char/k230_uart.h new file mode 100644 index 0000000000000000000000000000000000000000..049dcd341d3e81c861ce50bf681= 95d3c0e12cb40 --- /dev/null +++ b/include/hw/char/k230_uart.h @@ -0,0 +1,198 @@ +/* + * K230 UART device + * + * K230 Technical Reference Manual V0.3.1 (2024-11-18): + * https://github.com/revyos/external-docs/blob/master/K230/en-us/K230_Tec= hnical_Reference_Manual_V0.3.1_20241118.pdf + * + * Register semantics cross-checked against the SDK Linux driver + * src/little/linux/drivers/tty/serial/8250/8250_dw.c in + * https://github.com/kendryte/k230_sdk (compatible "snps,dw-apb-uart"). + * + * Copyright (c) 2026 WX Chen + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_K230_UART_H +#define HW_K230_UART_H + +#include "hw/core/sysbus.h" +#include "chardev/char-fe.h" +#include "hw/core/registerfields.h" +#include "qom/object.h" + +REG32(RBR_DLL_THR, 0x00) +REG32(IER_DLH, 0x04) +REG32(IER, 0x04) + FIELD(IER, ERBFI, 0, 1) + FIELD(IER, ETBEI, 1, 1) + FIELD(IER, ELSI, 2, 1) + FIELD(IER, EDSSI, 3, 1) + FIELD(IER, ELCOLR, 4, 1) + FIELD(IER, PTIME, 7, 1) +REG32(FCR, 0x08) + FIELD(FCR, FIFOE, 0, 1) + FIELD(FCR, RFIFOR, 1, 1) + FIELD(FCR, XFIFOR, 2, 1) + FIELD(FCR, DMAM, 3, 1) + FIELD(FCR, TET, 4, 2) + FIELD(FCR, RT, 6, 2) +REG32(IIR, 0x08) + FIELD(IIR, IID, 0, 4) + FIELD(IIR, FIFOSE, 6, 2) +REG32(LCR, 0x0c) + FIELD(LCR, DLS, 0, 2) + FIELD(LCR, STOP, 2, 1) + FIELD(LCR, PEN, 3, 1) + FIELD(LCR, EPS, 4, 1) + FIELD(LCR, SP, 5, 1) + FIELD(LCR, BC, 6, 1) + FIELD(LCR, DLAB, 7, 1) +REG32(MCR, 0x10) + FIELD(MCR, DTR, 0, 1) + FIELD(MCR, RTS, 1, 1) + FIELD(MCR, OUT1, 2, 1) + FIELD(MCR, OUT2, 3, 1) + FIELD(MCR, LOOPBACK, 4, 1) + FIELD(MCR, AFCE, 5, 1) + FIELD(MCR, SIRE, 6, 1) +REG32(LSR, 0x14) + FIELD(LSR, DR, 0, 1) + FIELD(LSR, OE, 1, 1) + FIELD(LSR, PE, 2, 1) + FIELD(LSR, FE, 3, 1) + FIELD(LSR, BI, 4, 1) + FIELD(LSR, THRE, 5, 1) + FIELD(LSR, TEMT, 6, 1) + FIELD(LSR, RFE, 7, 1) + FIELD(LSR, ADDR_RSVD, 8, 1) +REG32(MSR, 0x18) + FIELD(MSR, DCTS, 0, 1) + FIELD(MSR, DDSR, 1, 1) + FIELD(MSR, TERI, 2, 1) + FIELD(MSR, DDCD, 3, 1) + FIELD(MSR, CTS, 4, 1) + FIELD(MSR, DSR, 5, 1) + FIELD(MSR, RI, 6, 1) + FIELD(MSR, DCD, 7, 1) +REG32(RFW, 0x78) + FIELD(RFW, RFWD, 0, 8) + FIELD(RFW, RFPE, 8, 1) + FIELD(RFW, RFFE, 9, 1) +REG32(USR, 0x7c) + FIELD(USR, BUSY, 0, 1) + FIELD(USR, TFNF, 1, 1) + FIELD(USR, TFE, 2, 1) + FIELD(USR, RFNE, 3, 1) + FIELD(USR, RFF, 4, 1) +REG32(TFL, 0x80) + FIELD(TFL, TFL, 0, 5) +REG32(RFL, 0x84) + FIELD(RFL, RFL, 0, 5) +REG32(SRR, 0x88) + FIELD(SRR, UR, 0, 1) + FIELD(SRR, RFR, 1, 1) + FIELD(SRR, XFR, 2, 1) +REG32(SRTS, 0x8c) + FIELD(SRTS, SRTS, 0, 1) +REG32(SBCR, 0x90) + FIELD(SBCR, SBCB, 0, 1) +REG32(SDMAM, 0x94) + FIELD(SDMAM, SDMAM, 0, 1) +REG32(SFE, 0x98) + FIELD(SFE, SFE, 0, 1) +REG32(SRT, 0x9c) + FIELD(SRT, SRT, 0, 2) +REG32(STET, 0xa0) + FIELD(STET, STET, 0, 2) +REG32(HTX, 0xa4) + FIELD(HTX, HTX, 0, 1) +REG32(TCR, 0xac) + FIELD(TCR, RS485_EN, 0, 1) + FIELD(TCR, RE_POL, 1, 1) + FIELD(TCR, DE_POL, 2, 1) + FIELD(TCR, XFER_MODE, 3, 2) +REG32(SCR, 0x1c) +REG32(DLF, 0xc0) +REG32(CPR, 0xf4) +REG32(UCV, 0xf8) +REG32(CTR, 0xfc) + +/* peripheral ID 0x44570110 ("DW\x01\x10"). Read-only. */ +#define K230_UART_CTR_VALUE 0x44570110u +#define K230_UART_16550_COMPATIBLE 0 +#define K230_UART_FIFO_DEPTH 32 + +/* CPR */ +#define K230_UART_APB_DATA_WIDTH 2 /* CPR[1:0] - 32-bit APB = */ +#define K230_UART_AFCE_MODE 0 /* CPR[4] - not implemente= d */ +#define K230_UART_THRE_MODE 1 /* CPR[5] - implemented = */ +#define K230_UART_SIR_MODE 0 /* CPR[6] - not implemente= d */ +#define K230_UART_SIR_LP_MODE 0 /* CPR[7] - not implemente= d */ +#define K230_UART_ADDITIONAL_FEATURES 1 /* CPR[8] - UCV/CTR presen= t */ +#define K230_UART_FIFO_ACCESS 0 /* CPR[9] - not implemente= d */ +#define K230_UART_FIFO_STAT 1 /* CPR[10] - TFL/RFL presen= t */ +#define K230_UART_SHADOW 1 /* CPR[11] - shadow regs = */ +#define K230_UART_ADD_ENCODED_PARAMS 1 /* CPR[12] - CPR present = */ +#define K230_UART_DMA_EXTRA 0 /* CPR[13] - not implemente= d */ +#define K230_UART_FIFO_MODE 0x2 /* CPR[23:16] - 32-byte FIFO = */ + +/* CPR read-only value, assembled from the fields above. */ +#define K230_UART_R_CPR \ + ((uint32_t)(K230_UART_APB_DATA_WIDTH << 0) | \ + (K230_UART_AFCE_MODE << 4) | \ + (K230_UART_THRE_MODE << 5) | \ + (K230_UART_SIR_MODE << 6) | \ + (K230_UART_SIR_LP_MODE << 7) | \ + (K230_UART_ADDITIONAL_FEATURES << 8) | \ + (K230_UART_FIFO_ACCESS << 9) | \ + (K230_UART_FIFO_STAT << 10) | \ + (K230_UART_SHADOW << 11) | \ + (K230_UART_ADD_ENCODED_PARAMS << 12) | \ + (K230_UART_DMA_EXTRA << 13) | \ + (K230_UART_FIFO_MODE << 16)) + +/* UCV: ASCII "4.0a" =3D 0x34 0x2e 0x30 0x61 */ +#define K230_UART_R_UCV 0x342e3061u + +#define TYPE_K230_UART "k230-uart" +OBJECT_DECLARE_SIMPLE_TYPE(K230UartState, K230_UART) +struct K230UartState { + SysBusDevice parent_obj; + MemoryRegion mmio; + + /* Standard 16550 registers */ + uint8_t dll; /* Divisor Latch Low, offset 0x00 */ + uint8_t ier; /* Interrupt Enable, offset 0x04 */ + uint8_t dlh; /* Divisor Latch High, offset 0x04 */ + uint8_t fcr; /* FIFO Control, offset 0x08 */ + uint8_t iir; /* Interrupt Identification, offset 0x08 */ + uint8_t lcr; /* Line Control, offset 0x0c */ + uint8_t mcr; /* Modem Control, offset 0x10 */ + uint8_t lsr; /* Line Status, offset 0x14 */ + uint8_t msr; /* Modem Status, offset 0x18 */ + uint8_t scr; /* Scratchpad, offset 0x1c */ + + /* DesignWare-specific registers */ + uint8_t usr; /* UART Status, offset 0x7c */ + uint8_t htx; /* Halt TX, offset 0xa4 */ + + /* Internal interrupt state. */ + uint8_t thr_ipending; /* THR empty (IID=3D0x2) pending */ + uint8_t timeout_ipending; /* RX FIFO timeout (IID=3D0xc) */ + uint8_t busy_ipending; /* busy detect (IID=3D0x7) pending */ + + /* FIFO */ + uint16_t rx_fifo[K230_UART_FIFO_DEPTH]; + uint32_t rx_head, rx_tail, rx_count; + uint16_t tx_fifo[K230_UART_FIFO_DEPTH]; + uint32_t tx_head, tx_tail, tx_count; + + uint64_t char_transmit_time; + + CharFrontend chr; + qemu_irq irq; + QEMUTimer rx_timeout; +}; + +#endif --=20 2.48.1 From nobody Mon Sep 28 01:13:33 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=none dis=none) header.from=gmail.com ARC-Seal: i=1; a=rsa-sha256; t=1786156489; cv=none; d=zohomail.com; s=zohoarc; b=SVjZsDDn+qvxZfBr0sDVEejAH1nyCbTq7qqkSvmcO0Oj3UNznl16XihFNLYirFIST0VHXWoiZGbpiNnqG00UB4P8/pvSHzgLZO13pFEjGRmJhx+ZKJZGR8iyfRN1ZdKxWh569Cdb54KKNl1JOGcI9ixHpYxk4GRILa84oysQuDE= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1786156489; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=DrxZzHUwdc5+GfibuEQ6wnGR+ecsPhEIDC0zKF5DI/I=; b=hF7eLEfukVDrfJ0TQVv3Mz8yf2rJuPmI+JeFkGKHUwxxQKNGSYG5rwYxVxVNlE4OyZch2LQX5OwC1G4PxEo2EKLL/krczxQV9HuD8L+XTf5SD9+uZg5bjHNA6VAS9F4Z3kp7KAFMGVO+tqPVxlwzTb6ZeyQgKWClpHt8ee1eLUs= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists1p.gnu.org (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1786156489571393.6158337920707; Fri, 7 Aug 2026 19:34:49 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1wsWtD-0000H2-Mg; Fri, 07 Aug 2026 22:34:24 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists1p.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1wsWsy-0000Cb-IL for qemu-devel@nongnu.org; Fri, 07 Aug 2026 22:34:13 -0400 Received: from mail-pj1-x102c.google.com ([2607:f8b0:4864:20::102c]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1wsWsu-0007E9-41 for qemu-devel@nongnu.org; Fri, 07 Aug 2026 22:34:08 -0400 Received: by mail-pj1-x102c.google.com with SMTP id 98e67ed59e1d1-381b831d535so231817a91.0 for ; Fri, 07 Aug 2026 19:33:51 -0700 (PDT) Received: from [127.0.1.1] ([14.19.40.154]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-3925ff69eabsm4475835a91.15.2026.08.07.19.33.45 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 07 Aug 2026 19:33:49 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1786156430; x=1786761230; darn=nongnu.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=DrxZzHUwdc5+GfibuEQ6wnGR+ecsPhEIDC0zKF5DI/I=; b=CDvnCebMk9ad9pl21nIDkRiR5u3uVWSmHNFN6fOniH2qoa5rIXCuUkX1vK41anpNZ4 BFuuVFFv+6tAQ5GFuGz/LeVjj2kCamGr907WF8/qiEFaTwMC3JhGhahnH+um/2Gpg5Pp O8IZbIwuIk+WDhj99wfUSsC6b51hIzlPEh6E5xAbKM7RqPcayd2FupzRGyXs8ZP9zTuh SLLjq9CLh3lE2FUX78BONgRgThCd7F0Jd5TL7ESpq4y6VtNeICztsT94bZcqIaHa7uLY 0NttbwzVg2TxHU0anO+LYu0TZxCSPnxAhVlEgwLV/D1ozEQHHmtYor+deLrOC/JehMLk Y8+w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1786156430; x=1786761230; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to :content-type; bh=DrxZzHUwdc5+GfibuEQ6wnGR+ecsPhEIDC0zKF5DI/I=; b=ADQUIEd9gRcG1XtHN26ZWQu2R7ZOelJImxz+ZkGac9owQss0xsXsg4rcR7T6OcZDY9 Bxsva6RKuu1VAJ3+rwGEP951x5biQ41zUTM86v+qqmmdonu/fXavN2wNSFU2YKeduGhZ JN7rSfuqi8rO1ItNeyNs0BeVmO1anaRO5Llnb2t1CRUkBGK4p4EsuZqKslMT70RLpB+d Ok3vwfukpua3ZYsCeCql9I7N8GM3Uw88Dn8UJJzjzcTGEuodveW9zgewJ/dsuT50c/7k ZRVLRRVj/hlNS09V2/4kvdYYgpIo4b8cdu05z0/C1cWN+o1rQYgnjPHea3wtFcZ9lPsV X5MA== X-Gm-Message-State: AOJu0YyMZOFCcaWfOZaHdxBvmKxExM6DkDgPM3u6yqL6E7l/cDrwvSvQ DB0hd25rAEtp6KoYdK4gPXZs4heaI5R7r+9G5xtFa3eBllU26dWa7/sx X-Gm-Gg: AR+sD13oVsNpI6d4JlALVkCNl94B5b3oNWhRJKmT1ZaAOxkSXJ5G9kmp67ZbuiyiAO7 9/gkEoGGYAWXoq26bRKW8HIC0LN1htC/H3ZgNAnzbyGxdk1oD3906h28dAAX+Qx4gw71BQiSJmO S2nFsYBRm0nyhc7CoJiFnY1IZ3fuaI9GtGEwQ5TvM0PC6W+OHtn62Jfx7CAv2krljl7TkXHxTBZ YMlP9XFuXvcbaXkHTRea3ITaqHN30+4uozZkAqPJQN8esBoWLyTgawR7hKBF7QyrRFFUxUFm0rh sqCKfUC9ylYqTj1cEPgJXthgtNh4vn9n9E7HgJ6gWuJnxZMKgAvIr0VUVYVAU+GKDbyBddidf7X oFndPNXhh/Wi/qy1qgJRxTrhZ696t2vRl1RRfn1fanQ9iwYPFFFqDaoO0kBDRehwu3WcEvshXKu hUxblde+vesmanX7Y9GJElS2X1zRXJP7vn/YohAbrZ7yaLQzUp9lEULyuW5g== X-Received: by 2002:a17:90b:4c03:b0:38e:655c:6516 with SMTP id 98e67ed59e1d1-39261d10765mr6461637a91.0.1786156430391; Fri, 07 Aug 2026 19:33:50 -0700 (PDT) From: WX Chen Date: Sat, 08 Aug 2026 10:32:43 +0800 Subject: [PATCH v3 2/3] hw/riscv: k230: connect DW 8250 UART MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260808-feat-k230-uart-v3-v3-2-d73b31f0034b@gmail.com> References: <20260808-feat-k230-uart-v3-v3-0-d73b31f0034b@gmail.com> In-Reply-To: <20260808-feat-k230-uart-v3-v3-0-d73b31f0034b@gmail.com> To: qemu-devel@nongnu.org Cc: Paolo Bonzini , =?utf-8?q?Marc-Andr=C3=A9_Lureau?= , Chao Liu , Palmer Dabbelt , Alistair Francis , Weiwei Li , Daniel Henrique Barboza , Liu Zhiwei , qemu-riscv@nongnu.org, Fabiano Rosas , Laurent Vivier , WX Chen X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=ed25519-sha256; t=1786156416; l=4451; i=wxchen0913@gmail.com; s=20260725; h=from:subject:message-id; bh=asIegPxVJrAEDP8njRhzW1M1nb230OJXI4U9qipMm0o=; b=8FcruHZRT3nIK85M2aJlUgTIRtybHxD9ol3URb2PgNCophjIBSMnqFlDnVFWuYoFgYe/DC0kx T6NFG/l7o4xBYZHS9ZlsCveXNN+R06T4WEcLKINvcYZ6ERAXQfBZ2Hz X-Developer-Key: i=wxchen0913@gmail.com; a=ed25519; pk=FX63IRxmtz1BCNzHxOOWDPTo8L/D0YUxlIKc1WKi7wo= Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists1p.gnu.org; Received-SPF: pass client-ip=2607:f8b0:4864:20::102c; envelope-from=wxchen0913@gmail.com; helo=mail-pj1-x102c.google.com X-Spam_score_int: -17 X-Spam_score: -1.8 X-Spam_bar: - X-Spam_report: (-1.8 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FREEMAIL_ENVFROM_END_DIGIT=0.25, FREEMAIL_FROM=0.001, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @gmail.com) X-ZM-MESSAGEID: 1786156492134158500 Replace the generic serial-mm device with the K230 DW 8250-compatible UART model added in the previous patch. Signed-off-by: WX Chen Reviewed-by: Daniel Henrique Barboza --- hw/riscv/k230.c | 33 ++++++++++++++++++++++----------- include/hw/riscv/k230.h | 6 ++++-- 2 files changed, 26 insertions(+), 13 deletions(-) diff --git a/hw/riscv/k230.c b/hw/riscv/k230.c index 502281c52cff1dce6febb7487dfdefefe6363e9c..d49fa23448f38dfed45a570eb5b= ece64519bfe27 100644 --- a/hw/riscv/k230.c +++ b/hw/riscv/k230.c @@ -29,7 +29,6 @@ #include "hw/riscv/machines-qom.h" #include "hw/intc/riscv_aclint.h" #include "hw/intc/sifive_plic.h" -#include "hw/char/serial-mm.h" #include "hw/misc/unimp.h" =20 /* Align K230_SDK k230_canmv_defconfig */ @@ -111,6 +110,11 @@ static void k230_soc_init(Object *obj) object_initialize_child(obj, "k230-wdt0", &s->wdt[0], TYPE_K230_WDT); object_initialize_child(obj, "k230-wdt1", &s->wdt[1], TYPE_K230_WDT); =20 + for (int i =3D 0; i < K230_UART_COUNT; i++) { + g_autofree char *name =3D g_strdup_printf("k230-uart%d", i); + object_initialize_child(obj, name, &s->uart[i], TYPE_K230_UART); + } + qdev_prop_set_uint32(DEVICE(cpu0), "hartid-base", 0); qdev_prop_set_string(DEVICE(cpu0), "cpu-type", TYPE_RISCV_CPU_THEAD_C9= 08); qdev_prop_set_uint64(DEVICE(cpu0), "resetvec", @@ -136,19 +140,26 @@ static DeviceState *k230_create_plic(int base_hartid,= int hartid_count) memmap[K230_DEV_PLIC].size); } =20 -static void k230_create_uart(MemoryRegion *sys_mem, DeviceState *plic, - int index) +static void k230_create_uart(K230SoCState *s, DeviceState *plic, int index) { int uart_dev =3D K230_DEV_UART0 + index; - g_autofree char *name =3D g_strdup_printf("uart%d", index); + g_autofree char *unimpl_name =3D g_strdup_printf("uart%d", index); + DeviceState *dev =3D DEVICE(&s->uart[index]); =20 - /* Cover the non-16550 part of the SDK's 0x1000 UART window. */ - create_unimplemented_device(name, memmap[uart_dev].base, - memmap[uart_dev].size); + qdev_prop_set_chr(dev, "chardev", serial_hd(index)); =20 - serial_mm_init(sys_mem, memmap[uart_dev].base, 2, - qdev_get_gpio_in(plic, K230_UART0_IRQ + index), - 399193, serial_hd(index), DEVICE_LITTLE_ENDIAN); + if (!sysbus_realize(SYS_BUS_DEVICE(dev), &error_fatal)) { + return; + } + + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, memmap[uart_dev].base); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, + qdev_get_gpio_in(plic, K230_UART0_IRQ + index)); + + /* Cover the non-16550 part of the SDK's 0x1000 UART window. */ + create_unimplemented_device(unimpl_name, + memmap[uart_dev].base + 0x100, + memmap[uart_dev].size - 0x100); } =20 static void k230_soc_realize(DeviceState *dev, Error **errp) @@ -188,7 +199,7 @@ static void k230_soc_realize(DeviceState *dev, Error **= errp) =20 /* UART */ for (int i =3D 0; i < K230_UART_COUNT; i++) { - k230_create_uart(sys_mem, DEVICE(s->c908_plic), i); + k230_create_uart(s, DEVICE(s->c908_plic), i); } =20 /* Watchdog */ diff --git a/include/hw/riscv/k230.h b/include/hw/riscv/k230.h index 592e1c26bf8a8f8a1c66653ff9b568fbfe98a3ea..fed0357c2b9e1f5254cb157e0cd= 4d75d4946d1a1 100644 --- a/include/hw/riscv/k230.h +++ b/include/hw/riscv/k230.h @@ -17,10 +17,13 @@ =20 #include "hw/core/boards.h" #include "hw/riscv/riscv_hart.h" +#include "hw/char/k230_uart.h" #include "hw/watchdog/k230_wdt.h" =20 #define C908_CPU_HARTID (0) =20 +#define K230_UART_COUNT 5 + #define TYPE_RISCV_K230_SOC "riscv.k230.soc" #define RISCV_K230_SOC(obj) \ OBJECT_CHECK(K230SoCState, (obj), TYPE_RISCV_K230_SOC) @@ -32,6 +35,7 @@ typedef struct K230SoCState { /*< public >*/ RISCVHartArrayState c908_cpu; /* Small core */ =20 + K230UartState uart[K230_UART_COUNT]; K230WdtState wdt[2]; MemoryRegion sram; MemoryRegion bootrom; @@ -131,8 +135,6 @@ enum { K230_WDT1_IRQ =3D 108, }; =20 -#define K230_UART_COUNT 5 - /* * Integrates with the interrupt controller (PLIC), * which can process 208 interrupt external sources --=20 2.48.1 From nobody Mon Sep 28 01:13:33 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=none dis=none) header.from=gmail.com ARC-Seal: i=1; a=rsa-sha256; t=1786156489; cv=none; d=zohomail.com; s=zohoarc; b=NC6qCwc8iAvO5Wj4rH9DxSA1fiGf0sZ41uxaY8gP33jKqdDV3bePSUeC0KTBUCJy/D6X2CFvC79Lahg3zXBIkYCEtDYiQ4PCyLmEuhEgXAqH59BdNYGsrQF1A/OksY5qQxlfnD1ubcTOgeJ4wfjDUqajo45+nAudnLOCoxZ16Fk= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1786156489; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=Qq1GltB+RqYMbLpB7D9UbLnWlndbrmYdK/X3PzWj93A=; b=oFGx6Cp4PYvxqpBzROOPF46PP55g+2jY8hH4YsiM75VM+Q596zHZnOsQuVgvDyruvm0SXoOjBrlHKvietiqMETPz6TCQxM8+JkanJuZcBgUPZrBnLFtLhc9pBN89OCDLMszUAvTN9MSCoaLjgODluGu6IwA6htD7Z4RnFYsmP0E= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists1p.gnu.org (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1786156489138497.6242086467395; Fri, 7 Aug 2026 19:34:49 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1wsWtD-0000HB-Mk; Fri, 07 Aug 2026 22:34:24 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists1p.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1wsWt2-0000Df-0U for qemu-devel@nongnu.org; Fri, 07 Aug 2026 22:34:13 -0400 Received: from mail-pg1-x52f.google.com ([2607:f8b0:4864:20::52f]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1wsWsw-0007Lx-73 for qemu-devel@nongnu.org; Fri, 07 Aug 2026 22:34:11 -0400 Received: by mail-pg1-x52f.google.com with SMTP id 41be03b00d2f7-caf45fc5202so65542a12.1 for ; Fri, 07 Aug 2026 19:33:56 -0700 (PDT) Received: from [127.0.1.1] ([14.19.40.154]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-3925ff69eabsm4475835a91.15.2026.08.07.19.33.50 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 07 Aug 2026 19:33:54 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1786156435; x=1786761235; darn=nongnu.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=Qq1GltB+RqYMbLpB7D9UbLnWlndbrmYdK/X3PzWj93A=; b=i4FaCz8Z2rtqfPEG6Stg4mKbhQ3UHhB1/eWMt8T7X/VJzxFXJ0o5S7DGYvpeuBgqA/ QfUKLpOwpFzxYWzR02I6AZrgOASYUV12Mjf0lIIgRTX6WR6mFinsHJRqVZ6RviVJC5t7 94luKjGugYYhd8JdsqrwEy8Svsqvk1V3O5Gskwq80Oe/AjbOsCECjsIH5VfNnkl0NSIV YLcRJd2CFBBmfZX77N06CSPJgxominZvO0FT6BvJOWxAEXUJ81zip3mMkxWcnfVXLbnf f69NGBh3O32cLFniSp95Wz7xSo8DFTyHj6f8zz4uHOgYdEw9eM5HDkuEJtWO9eUtE/R/ sj9A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1786156435; x=1786761235; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to :content-type; bh=Qq1GltB+RqYMbLpB7D9UbLnWlndbrmYdK/X3PzWj93A=; b=pST1IkhE02yLmpPLtDEOvaMUJ7v3yoI9ySZ/2iXIvgvuBbRUfQ7MD5M14ycSfX20mK xdwr70jacHcVMKPuBvkFoWHR/NJE2LpQM4ptUmYsbtbTZoA0aj7lCle6m3/Jf0ofZWuD y4xhvYc61iicz3QVdUEPr//zB6AzfuuYqrcxAqOP0I0pMfTjntxOcuVJqbdAf1TbCDqU EWOlVez9d5miE3H+LRBc0dVYW7fzWixSj5mDyFIzUQrSa6n/um6OANRjG2EltP9zidyK 2TPASlqUBGI+UXCq9w4CxZuHkZpKo9IP2wy4hfLEg3h+VikndTT7kR98lY+iY40Ai1pe zHRw== X-Gm-Message-State: AOJu0YwB9gBbib1B/q1RhWHwqg2OpDrTytzIs9eJzbDcGxcW8wr7nXyA vZRTkU/NFJ0XpivAU8Ig3YnX4AxaF5BbxyOn6ukwYKsfBhzV5TOurn5H X-Gm-Gg: AR+sD10UOkTU9xqziwxWqlpUbYr1lq4igTxzwdlYE5UwM/a2zg4+LY07fkWpMsgy1ST rnl4HHLpyTcFZ7cWaa46uXMI0bOqfcVGognaLG4es1RDBrAvNmdaQMatyJCcREMVNduHHbo/fyG 6V73fxtzAOyfAyqt9u0zRbXu4qREsoFK7oZpjTWd4kkoxnYHW6N0/p2xywLd0PcZXDm2zzH+ZRM aWNKzHa2da2FRsuGb4kRNZpTeO5esQhDyW8Z6S+LJ1UGi7dm3lpb1EDPFdJOF/+a0EcCVTQOUEE oNwUO+LHX/j45/ctZZI8VSxyG2xugx7+02Quq9LMa0zc3HcjtTwlOqn0Nn6Gl3x37RjBoWYR2Nk rmSbqyUYot0WgMNycsU+Xnr24SC+8QPWuUt/4BTzcvUfKs63FUaSa8Vg4QyQucrhsR8SRl2VpiX 5qtpkrkofpHYcyff0D5Jmby0es7ct7KQoI9Cvj84kblA/e+WMDP0GDXaRQt+S56s61PYc2QA== X-Received: by 2002:a17:90b:1d02:b0:37f:fdc8:71b4 with SMTP id 98e67ed59e1d1-3903c599356mr27805927a91.2.1786156434791; Fri, 07 Aug 2026 19:33:54 -0700 (PDT) From: WX Chen Date: Sat, 08 Aug 2026 10:32:44 +0800 Subject: [PATCH v3 3/3] tests/qtest: add K230 UART test MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260808-feat-k230-uart-v3-v3-3-d73b31f0034b@gmail.com> References: <20260808-feat-k230-uart-v3-v3-0-d73b31f0034b@gmail.com> In-Reply-To: <20260808-feat-k230-uart-v3-v3-0-d73b31f0034b@gmail.com> To: qemu-devel@nongnu.org Cc: Paolo Bonzini , =?utf-8?q?Marc-Andr=C3=A9_Lureau?= , Chao Liu , Palmer Dabbelt , Alistair Francis , Weiwei Li , Daniel Henrique Barboza , Liu Zhiwei , qemu-riscv@nongnu.org, Fabiano Rosas , Laurent Vivier , WX Chen X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=ed25519-sha256; t=1786156416; l=17851; i=wxchen0913@gmail.com; s=20260725; h=from:subject:message-id; bh=Pjoeg6icFvXO56//Vt79+felKYLQvRr4fkFPGGj4gT4=; b=16L4zqLGm1aBxmHgqkcW6jkXcjf27Wl+76DZOFtn19UDbjQ4EhypoO32U7KuYj1+u65FqywNK X0l5iUc68xDBTN+ija0MFukJOnuCg9EDDVkp02ViEbID83LfPZqFrq2 X-Developer-Key: i=wxchen0913@gmail.com; a=ed25519; pk=FX63IRxmtz1BCNzHxOOWDPTo8L/D0YUxlIKc1WKi7wo= Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists1p.gnu.org; Received-SPF: pass client-ip=2607:f8b0:4864:20::52f; envelope-from=wxchen0913@gmail.com; helo=mail-pg1-x52f.google.com X-Spam_score_int: -17 X-Spam_score: -1.8 X-Spam_bar: - X-Spam_report: (-1.8 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FREEMAIL_ENVFROM_END_DIGIT=0.25, FREEMAIL_FROM=0.001, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=unavailable autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @gmail.com) X-ZM-MESSAGEID: 1786156492393158500 Add 8 test cases covering the main register paths and driver usage scenarios: device probe, init & baud, TX/RX datapath, THRE interrupt, RX interrupts (timeout & trigger level), error interrupts & IIR priority, USR & busy detect, and advanced features (shadow registers, HTX, SRR, PTIME/TET). All tests pass. Signed-off-by: WX Chen Reviewed-by: Daniel Henrique Barboza --- tests/qtest/k230-uart-test.c | 514 +++++++++++++++++++++++++++++++++++++++= ++++ tests/qtest/meson.build | 2 +- 2 files changed, 515 insertions(+), 1 deletion(-) diff --git a/tests/qtest/k230-uart-test.c b/tests/qtest/k230-uart-test.c new file mode 100644 index 0000000000000000000000000000000000000000..488dbbb7ffd28726434cdfebe97= 6c3155e14b455 --- /dev/null +++ b/tests/qtest/k230-uart-test.c @@ -0,0 +1,514 @@ +/* + * QTest for the K230 UART =E2=80=94 functional-path coverage. + * + * Tests are organised around driver usage scenarios rather than + * enumerating every register in isolation. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include + +#define UART_BASE 0x91400000 +#define R(off) (UART_BASE + (off)) + +/* register offsets */ +#define THR 0x00 +#define IER 0x04 +#define IIR 0x08 +#define LCR 0x0c +#define MCR 0x10 +#define LSR 0x14 +#define SCR 0x1c +#define USR 0x7c +#define TFL 0x80 +#define RFL 0x84 +#define SRR 0x88 +#define SRTS 0x8c +#define SBCR 0x90 +#define SDMAM 0x94 +#define SFE 0x98 +#define SRT 0x9c +#define STET 0xa0 +#define HTX 0xa4 +#define CPR 0xf4 +#define CTR 0xfc + +/* bit fields */ +#define LCR_DLAB 0x80 +#define LCR_BC 0x40 +#define LCR_8N1 0x03 + +#define LSR_DR 0x01 +#define LSR_OE 0x02 +#define LSR_THRE 0x20 +#define LSR_TEMT 0x40 +#define LSR_RESET 0x60 + +#define IIR_IID 0x0f +#define IIR_NONE 0x01 +#define IIR_THR 0x02 +#define IIR_RX 0x04 +#define IIR_LINE 0x06 +#define IIR_BUSY 0x07 +#define IIR_TO 0x0c +#define IIR_FF 0xc0 + +#define IER_RX 0x01 +#define IER_TX 0x02 +#define IER_LS 0x04 +#define IER_COLR 0x10 +#define IER_PTIME 0x80 + +#define MCR_LB 0x10 +#define MCR_RTS 0x02 + +#define FCR_FE 0x01 +#define FCR_RR 0x02 +#define FCR_XR 0x04 +#define FCR_TET_H (3 << 4) +#define FCR_RT_Q (1 << 6) +#define FCR_RT_F (3 << 6) + +#define USR_BUSY 0x01 +#define USR_RESET 0x06 + +#define SRR_UR 0x01 +#define SRR_RFR 0x02 + +/* helpers */ +static uint32_t rd(QTestState *qts, uint32_t o) +{ + return qtest_readl(qts, R(o)); +} +static uint32_t iid(QTestState *qts) +{ + return rd(qts, IIR) & IIR_IID; +} + +static void poll_lsr(QTestState *qts, uint32_t m) +{ + int i; + + for (i =3D 0; i < 1000; i++) { + if (rd(qts, LSR) & m) { + return; + } + qtest_clock_step(qts, 1000); + } + g_assert_not_reached(); +} + +static void s1(QTestState *qts, int fd, char c) +{ + g_assert_cmpint(send(fd, &c, 1, 0), =3D=3D, 1); + poll_lsr(qts, LSR_DR); +} + +static void sn(QTestState *qts, int fd, const char *d, int n) +{ + g_assert_cmpint(send(fd, d, n, 0), =3D=3D, n); + poll_lsr(qts, LSR_DR); +} + +static void oe_nf(QTestState *qts, int fd) +{ + int i; + + s1(qts, fd, 'A'); + { + char c =3D 'B'; + g_assert_cmpint(send(fd, &c, 1, 0), =3D=3D, 1); + } + for (i =3D 0; i < 200; i++) { + rd(qts, SCR); + qtest_clock_step(qts, 1000); + } +} + +/* 1. device probe */ +static void test_device_probe(void) +{ + QTestState *qts =3D qtest_init("-machine k230"); + uint32_t cpr =3D rd(qts, CPR); + + g_assert_cmphex(cpr & 0x3, =3D=3D, 0x2); + g_assert_cmphex(cpr & (1 << 5), =3D=3D, (1 << 5)); + g_assert_cmphex(cpr & (1 << 8), =3D=3D, (1 << 8)); + g_assert_cmphex((cpr >> 16) & 0xff, =3D=3D, 0x2); + g_assert_cmphex(cpr & (1 << 4), =3D=3D, 0); + g_assert_cmphex(rd(qts, CTR), =3D=3D, 0x44570110); + g_assert_cmphex(rd(qts, LSR), =3D=3D, LSR_RESET); + g_assert_cmphex(rd(qts, USR), =3D=3D, USR_RESET); + g_assert_cmphex(iid(qts), =3D=3D, IIR_NONE); + g_assert_cmphex(rd(qts, IIR) & IIR_FF, =3D=3D, 0); + g_assert_cmphex(rd(qts, USR) & USR_BUSY, =3D=3D, 0); + + qtest_writel(qts, R(IIR), FCR_FE); + g_assert_cmphex(rd(qts, IIR) & IIR_FF, =3D=3D, IIR_FF); + qtest_quit(qts); +} + +/* 2. init & baud */ +static void test_init_and_baud(void) +{ + int fd; + QTestState *qts =3D qtest_init_with_serial("-machine k230", &fd); + + qtest_writel(qts, R(LCR), LCR_DLAB); + qtest_writel(qts, R(THR), 0x55); + g_assert_cmphex(rd(qts, THR), =3D=3D, 0x55); + qtest_writel(qts, R(LCR), LCR_8N1); + g_assert_cmphex(rd(qts, THR), =3D=3D, 0x00); + + qtest_writel(qts, R(IIR), FCR_FE | FCR_RT_F); + qtest_writel(qts, R(IER), IER_RX); + + /* divisor=3D1 -> timeout=3D12800ns */ + qtest_writel(qts, R(LCR), LCR_8N1 | LCR_DLAB); + qtest_writel(qts, R(THR), 1); qtest_writel(qts, R(IER), 0); + qtest_writel(qts, R(LCR), LCR_8N1); + s1(qts, fd, 'X'); + qtest_clock_step(qts, 13000); + g_assert_cmphex(iid(qts), =3D=3D, IIR_TO); + + /* divisor=3D100 -> timeout=3D1.28e6ns; 13000ns too short */ + rd(qts, THR); + qtest_writel(qts, R(LCR), LCR_8N1 | LCR_DLAB); + qtest_writel(qts, R(THR), 100); qtest_writel(qts, R(IER), 0); + qtest_writel(qts, R(LCR), LCR_8N1); + s1(qts, fd, 'Y'); + qtest_clock_step(qts, 13000); + g_assert_cmphex(iid(qts), !=3D, IIR_TO); + qtest_clock_step(qts, 1300000); + g_assert_cmphex(iid(qts), =3D=3D, IIR_TO); + + close(fd); qtest_quit(qts); +} + +/* 3. TX / RX datapath */ +static void test_tx_rx_datapath(void) +{ + int fd; + QTestState *qts =3D qtest_init_with_serial("-machine k230", &fd); + + qtest_writel(qts, R(LCR), LCR_8N1); + g_assert_cmphex(rd(qts, LSR) & (LSR_THRE | LSR_TEMT), + =3D=3D, LSR_THRE | LSR_TEMT); + qtest_writel(qts, R(THR), 'A'); + g_assert_cmphex(rd(qts, LSR) & (LSR_THRE | LSR_TEMT), + =3D=3D, LSR_THRE | LSR_TEMT); + + /* external RX (FIFO mode) */ + qtest_writel(qts, R(IIR), FCR_FE); + qtest_writel(qts, R(IER), IER_RX); + sn(qts, fd, "K230", 4); + for (int i =3D 0; i < 4; i++) { + if (i < 3) { + g_assert_cmphex(iid(qts), =3D=3D, IIR_RX); + } + g_assert_cmphex(rd(qts, THR), =3D=3D, "K230"[i]); + } + g_assert_cmphex(rd(qts, LSR) & LSR_DR, =3D=3D, 0); + + /* loopback */ + qtest_writel(qts, R(MCR), MCR_LB); + qtest_writel(qts, R(THR), 'L'); + g_assert_cmphex(rd(qts, THR), =3D=3D, 'L'); + g_assert_cmphex(rd(qts, LSR) & LSR_DR, =3D=3D, 0); + + /* no loopback */ + qtest_writel(qts, R(MCR), 0); + qtest_writel(qts, R(THR), 'Z'); + g_assert_cmphex(rd(qts, LSR) & LSR_DR, =3D=3D, 0); + + /* TFL / RFL */ + qtest_writel(qts, R(MCR), MCR_LB); + qtest_writel(qts, R(IIR), FCR_FE | FCR_RR | FCR_XR); + qtest_writel(qts, R(THR), 'X'); qtest_writel(qts, R(THR), 'Y'); + g_assert_cmphex(rd(qts, TFL), =3D=3D, 0); + g_assert_cmphex(rd(qts, RFL), =3D=3D, 2); + + /* non-FIFO mode */ + qtest_writel(qts, R(MCR), 0); + qtest_writel(qts, R(IIR), 0); + s1(qts, fd, 'N'); + g_assert_cmphex(rd(qts, THR), =3D=3D, 'N'); + g_assert_cmphex(rd(qts, LSR) & LSR_DR, =3D=3D, 0); + + /* non-FIFO overrun */ + int i; + s1(qts, fd, 'n'); + g_assert_cmphex(rd(qts, LSR) & LSR_DR, =3D=3D, LSR_DR); + { + char c =3D 'o'; + send(fd, &c, 1, 0); + } + for (i =3D 0; i < 200; i++) { + rd(qts, SCR); + qtest_clock_step(qts, 1000); + } + g_assert_cmphex(rd(qts, LSR) & LSR_OE, =3D=3D, LSR_OE); + + close(fd); qtest_quit(qts); +} + +/* 4. THRE interrupt */ +static void test_thre_interrupt(void) +{ + QTestState *qts =3D qtest_init("-machine k230 " + "-chardev null,id=3Dc0 -serial chardev:c0= "); + qtest_writel(qts, R(LCR), LCR_8N1); + + qtest_writel(qts, R(THR), 'A'); + g_assert_cmphex(iid(qts), =3D=3D, IIR_NONE); + + qtest_writel(qts, R(IER), IER_TX); + g_assert_cmphex(iid(qts), =3D=3D, IIR_THR); + + qtest_writel(qts, R(THR), 'B'); + g_assert_cmphex(iid(qts), =3D=3D, IIR_THR); + + g_assert_cmphex(iid(qts), =3D=3D, IIR_NONE); + qtest_quit(qts); +} + +/* 5. RX interrupts: timeout & trigger level */ +static void test_rx_interrupts(void) +{ + int fd; + QTestState *qts =3D qtest_init_with_serial("-machine k230", &fd); + + qtest_writel(qts, R(LCR), LCR_8N1); + qtest_writel(qts, R(IIR), FCR_FE | FCR_RT_F); + qtest_writel(qts, R(IER), IER_RX); + + /* basic timeout */ + s1(qts, fd, 'T'); + qtest_clock_step(qts, 400000); + g_assert_cmphex(iid(qts), =3D=3D, IIR_TO); + g_assert_cmphex(rd(qts, THR), =3D=3D, 'T'); + g_assert_cmphex(iid(qts), !=3D, IIR_TO); + + /* timeout reset by new byte */ + s1(qts, fd, 'A'); qtest_clock_step(qts, 200000); + s1(qts, fd, 'B'); + qtest_clock_step(qts, 200000); + g_assert_cmphex(iid(qts), =3D=3D, IIR_NONE); + qtest_clock_step(qts, 200000); + g_assert_cmphex(iid(qts), =3D=3D, IIR_TO); + rd(qts, THR); + rd(qts, THR); + + /* timeout rearmed by partial drain */ + s1(qts, fd, 'X'); + s1(qts, fd, 'Y'); + qtest_clock_step(qts, 400000); + g_assert_cmphex(iid(qts), =3D=3D, IIR_TO); + g_assert_cmphex(rd(qts, THR), =3D=3D, 'X'); + qtest_clock_step(qts, 200000); + g_assert_cmphex(iid(qts), !=3D, IIR_TO); + qtest_clock_step(qts, 250000); + g_assert_cmphex(iid(qts), =3D=3D, IIR_TO); + g_assert_cmphex(rd(qts, THR), =3D=3D, 'Y'); + + /* RX trigger level: RT=3DQ -> trigger at 8 bytes */ + qtest_writel(qts, R(IIR), FCR_FE | FCR_RT_Q); + sn(qts, fd, "ABCDEFG", 7); + qtest_clock_step(qts, 20000); + g_assert_cmphex(iid(qts), =3D=3D, IIR_NONE); + qtest_clock_step(qts, 400000); + g_assert_cmphex(iid(qts), =3D=3D, IIR_TO); + for (int i =3D 0; i < 7; i++) { + rd(qts, THR); + } + + sn(qts, fd, "12345678", 8); + for (int i =3D 0; i < 1000; i++) { + if (iid(qts) =3D=3D IIR_RX) { + break; + } + qtest_clock_step(qts, 1000); + } + g_assert_cmphex(iid(qts), =3D=3D, IIR_RX); + + close(fd); qtest_quit(qts); +} + +/* 6. error interrupts & IIR priority */ +static void test_error_interrupts(void) +{ + int fd; + QTestState *qts =3D qtest_init_with_serial("-machine k230", &fd); + + qtest_writel(qts, R(LCR), LCR_8N1); + qtest_writel(qts, R(IER), IER_LS); + + /* OE -> IIR=3D0x6; LSR clears; ELCOLR=3D0: RBR clears; ELCOLR=3D1: RB= R keeps */ + oe_nf(qts, fd); + g_assert_cmphex(iid(qts), =3D=3D, IIR_LINE); + rd(qts, LSR); + g_assert_cmphex(iid(qts), !=3D, IIR_LINE); + + oe_nf(qts, fd); + g_assert_cmphex(iid(qts), =3D=3D, IIR_LINE); + rd(qts, THR); + g_assert_cmphex(iid(qts), !=3D, IIR_LINE); + + qtest_writel(qts, R(IER), IER_LS | IER_COLR); + oe_nf(qts, fd); + g_assert_cmphex(iid(qts), =3D=3D, IIR_LINE); + rd(qts, THR); + g_assert_cmphex(iid(qts), =3D=3D, IIR_LINE); + rd(qts, LSR); + g_assert_cmphex(iid(qts), !=3D, IIR_LINE); + + /* IIR priority: RX > TX (loopback) */ + qtest_writel(qts, R(IIR), FCR_FE); + qtest_writel(qts, R(MCR), MCR_LB); + qtest_writel(qts, R(IER), IER_TX | IER_RX); + qtest_writel(qts, R(THR), 'P'); + g_assert_cmphex(iid(qts), =3D=3D, IIR_RX); + g_assert_cmphex(rd(qts, THR), =3D=3D, 'P'); + g_assert_cmphex(iid(qts), =3D=3D, IIR_THR); + iid(qts); + g_assert_cmphex(iid(qts), =3D=3D, IIR_NONE); + + /* OE in FIFO mode via loopback */ + qtest_writel(qts, R(IER), IER_LS); + for (int i =3D 0; i < 32; i++) { + qtest_writel(qts, R(THR), 'a'); + } + g_assert_cmphex(rd(qts, LSR) & LSR_OE, =3D=3D, 0); + qtest_writel(qts, R(THR), 'z'); + g_assert_cmphex(iid(qts), =3D=3D, IIR_LINE); + g_assert_cmphex(rd(qts, LSR) & LSR_OE, =3D=3D, LSR_OE); + + close(fd); qtest_quit(qts); +} + +/* 7. USR & busy detect */ +static void test_busy_detect(void) +{ + int fd; + QTestState *qts =3D qtest_init_with_serial("-machine k230", &fd); + + qtest_writel(qts, R(LCR), LCR_8N1); + qtest_writel(qts, R(IIR), FCR_FE); + + g_assert_cmphex(rd(qts, USR) & 0x1e, =3D=3D, 0x06); + + qtest_writel(qts, R(MCR), MCR_LB); + qtest_writel(qts, R(THR), 'A'); + g_assert_cmphex(rd(qts, USR) & 0x08, =3D=3D, 0x08); + rd(qts, THR); + g_assert_cmphex(rd(qts, USR) & 0x08, =3D=3D, 0); + qtest_writel(qts, R(MCR), 0); + + /* BUSY via RX & loopback */ + g_assert_cmphex(rd(qts, USR) & USR_BUSY, =3D=3D, 0); + s1(qts, fd, 'Z'); + g_assert_cmphex(rd(qts, USR) & USR_BUSY, =3D=3D, USR_BUSY); + rd(qts, THR); + g_assert_cmphex(rd(qts, USR) & USR_BUSY, =3D=3D, 0); + + qtest_writel(qts, R(MCR), MCR_LB); + qtest_writel(qts, R(THR), 'L'); + g_assert_cmphex(rd(qts, USR) & USR_BUSY, =3D=3D, USR_BUSY); + rd(qts, THR); + g_assert_cmphex(rd(qts, USR) & USR_BUSY, =3D=3D, 0); + + /* busy-detect: LCR write rejected while BUSY=3D1 */ + qtest_writel(qts, R(MCR), 0); + qtest_writel(qts, R(IIR), FCR_FE | FCR_RR | FCR_XR); + s1(qts, fd, 'B'); + g_assert_cmphex(rd(qts, USR) & USR_BUSY, =3D=3D, USR_BUSY); + qtest_writel(qts, R(LCR), LCR_8N1 | LCR_DLAB); + g_assert_cmphex(rd(qts, LCR), =3D=3D, LCR_8N1); + g_assert_cmphex(iid(qts), =3D=3D, IIR_BUSY); + rd(qts, USR); + g_assert_cmphex(iid(qts), =3D=3D, IIR_NONE); + qtest_writel(qts, R(IIR), FCR_FE | FCR_RR); + g_assert_cmphex(rd(qts, USR) & USR_BUSY, =3D=3D, 0); + qtest_writel(qts, R(LCR), LCR_8N1 | LCR_DLAB); + g_assert_cmphex(rd(qts, LCR), =3D=3D, LCR_8N1 | LCR_DLAB); + + close(fd); qtest_quit(qts); +} + +/* 8. advanced features */ +static void test_advanced_features(void) +{ + QTestState *qts =3D qtest_init("-machine k230 " + "-chardev null,id=3Dc0 -serial chardev:c0= "); + + /* shadow: SRTS<->MCR.RTS, SBCR<->LCR.BC, SDMAM, SFE, SRT, STET, HTX */ + qtest_writel(qts, R(SRTS), 1); + g_assert_cmphex(rd(qts, MCR) & MCR_RTS, =3D=3D, MCR_RTS); + qtest_writel(qts, R(MCR), 0); g_assert_cmphex(rd(qts, SRTS), =3D=3D, 0= ); + qtest_writel(qts, R(SBCR), 1); + g_assert_cmphex(rd(qts, LCR) & LCR_BC, =3D=3D, LCR_BC); + qtest_writel(qts, R(LCR), 0); g_assert_cmphex(rd(qts, SBCR), =3D=3D, 0= ); + qtest_writel(qts, R(SDMAM), 1); g_assert_cmphex(rd(qts, SDMAM), =3D=3D= , 1); + qtest_writel(qts, R(SRT), 0x2); g_assert_cmphex(rd(qts, SRT), =3D=3D, = 0x2); + qtest_writel(qts, R(STET), 0x3); g_assert_cmphex(rd(qts, STET), =3D=3D= , 0x3); + qtest_writel(qts, R(SFE), 1); g_assert_cmphex(rd(qts, SFE), =3D=3D, 1); + g_assert_cmphex(rd(qts, IIR) & IIR_FF, =3D=3D, IIR_FF); + qtest_writel(qts, R(HTX), 1); g_assert_cmphex(rd(qts, HTX), =3D=3D, 1); + qtest_writel(qts, R(HTX), 0); g_assert_cmphex(rd(qts, HTX), =3D=3D, 0); + + /* HTX halt TX */ + qtest_writel(qts, R(LCR), LCR_8N1); + qtest_writel(qts, R(IIR), FCR_FE); + qtest_writel(qts, R(MCR), MCR_LB); + + qtest_writel(qts, R(HTX), 1); + qtest_writel(qts, R(THR), 'H'); + g_assert_cmphex(rd(qts, TFL), =3D=3D, 1); + g_assert_cmphex(rd(qts, RFL), =3D=3D, 0); + + qtest_writel(qts, R(HTX), 0); + g_assert_cmphex(rd(qts, TFL), =3D=3D, 0); + g_assert_cmphex(rd(qts, RFL), =3D=3D, 1); + g_assert_cmphex(rd(qts, THR), =3D=3D, 'H'); + + /* SRR: RFR & UR */ + qtest_writel(qts, R(THR), 'Z'); + g_assert_cmphex(rd(qts, RFL), =3D=3D, 1); + g_assert_cmphex(rd(qts, LSR) & LSR_DR, =3D=3D, LSR_DR); + qtest_writel(qts, R(SRR), SRR_RFR); + g_assert_cmphex(rd(qts, RFL), =3D=3D, 0); + g_assert_cmphex(rd(qts, LSR) & LSR_DR, =3D=3D, 0); + g_assert_cmphex(rd(qts, SRR), =3D=3D, 0); + qtest_writel(qts, R(THR), 'Y'); + qtest_writel(qts, R(SRR), SRR_UR); + g_assert_cmphex(rd(qts, LSR), =3D=3D, LSR_RESET); + g_assert_cmphex(iid(qts), =3D=3D, IIR_NONE); + g_assert_cmphex(rd(qts, RFL), =3D=3D, 0); + + /* PTIME + TET programmable THRE */ + qtest_writel(qts, R(LCR), LCR_8N1); + qtest_writel(qts, R(IIR), FCR_FE | FCR_TET_H); + qtest_writel(qts, R(IER), IER_TX | IER_PTIME); + qtest_writel(qts, R(THR), 'A'); + g_assert_cmphex(rd(qts, LSR) & LSR_THRE, =3D=3D, LSR_THRE); + g_assert_cmphex(iid(qts), =3D=3D, IIR_THR); + + qtest_quit(qts); +} + +int main(int argc, char *argv[]) +{ + g_test_init(&argc, &argv, NULL); + qtest_add_func("/k230-uart/device_probe", test_device_probe); + qtest_add_func("/k230-uart/init_and_baud", test_init_and_baud); + qtest_add_func("/k230-uart/tx_rx_datapath", test_tx_rx_datapath); + qtest_add_func("/k230-uart/thre_interrupt", test_thre_interrupt); + qtest_add_func("/k230-uart/rx_interrupts", test_rx_interrupts); + qtest_add_func("/k230-uart/error_interrupts", test_error_interrupts); + qtest_add_func("/k230-uart/busy_detect", test_busy_detect); + qtest_add_func("/k230-uart/advanced_features", test_advanced_features= ); + return g_test_run(); +} diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index 822e0bd286970339fe28127649feb131ce8a81b2..93246d9bf77206e9cebb9d8ade5= 801f4541d3b8d 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -294,7 +294,7 @@ qtests_riscv64 =3D ['riscv-csr-test'] + \ (config_all_devices.has_key('CONFIG_IOMMU_TESTDEV') and config_all_devices.has_key('CONFIG_RISCV_IOMMU') ? ['iommu-riscv-test'] : []) + \ - (config_all_devices.has_key('CONFIG_K230') ? ['k230-wdt-test'] : []) + (config_all_devices.has_key('CONFIG_K230') ? ['k230-wdt-test', 'k230-uar= t-test'] : []) =20 qtests_hexagon =3D ['boot-serial-test'] =20 --=20 2.48.1