From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-ej1-f46.google.com (mail-ej1-f46.google.com [209.85.218.46]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 7A14A4C6527 for ; Sun, 18 Oct 2026 13:49:52 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.218.46 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1792331394; cv=none; b=eTEZhtB4eIGAKK8jZdj/2YRXOubUyYwkuDOs4ugVGFaYaqFkFVbPIsH5QM4u1GmbpE+HnDGJo7OELIfyDwWbMz7DNHbgljGiUA9iXrMhhmo7/RqN9Qzzu8I1SPP8eQKuQCM3bWmtRDbHVQwJMx+VEFmGHPR6eNALRiW7Fyi8doo= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1792331394; c=relaxed/simple; bh=6bijUlXAbL/N3wCEuzy67Wc9+cqaf06lnVeWNhLXdQQ=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Zf/vxlUdz+KUTAQp3sH8/LpHH0z7CfVgl5sLzqDo+/W6lO7ohLd9hcM2f2sp+w/nZreXFq/So5ItVRZETw/LDli/1YfQORVrb9Sag2DlN4tqmRjg8ug0hOai19UeADR0eB8G2fQGSmHZYS7puzzR0Hdz4oI/WgsBL7mJwB35WiY= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=iqTMWcyH; arc=none smtp.client-ip=209.85.218.46 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="iqTMWcyH" Received: by mail-ej1-f46.google.com with SMTP id a640c23a62f3a-c2e8c710847so149271366b.0 for ; Sun, 18 Oct 2026 06:49:52 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1792331390; x=1792936190; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=lpRhgnnQ3c69BKtA52SSfn2OKHjIwq2qtwMcYorMrto=; b=iqTMWcyHWkDCeh3FocsbKTRCJWMW64g5M/SZwnro/CqvKjBIaBBh6tXfeRcM7JaL5R HUb52wqCHkzPDXvfwfiKseKOo0k3Mc3FeSWceSObZiao/aMgVD4sqR1G/ghXXCwMxyvG 9XDsDc6cFg5bCu3UFETpF3wIoLCt/YdPgjmgeRUXxdevcQJ+RsklgMFNDquBh9Z0/jie qvqBhlRuXsK0RrTQCv4qxF2GuGovrLMXQcayaN4jQJCdv8fT2PSjtAuXqCbzl+d/slUe ZsnA9G4nBJ5SDSoA4+RO1xF5Q/RD8pZj7CRITUEBePdpHXDzYLWSSB4qVGD6b8NU9Mhz 54zQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1792331390; x=1792936190; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=lpRhgnnQ3c69BKtA52SSfn2OKHjIwq2qtwMcYorMrto=; b=XZtlXZ69DXBy/JmLf40M3hpjRywRlDERo9Nhfgq9gYP4oNJqINy+aTy95B6KgiadMI 5tAfbojFVlsxyluJt+3IQRSEEwXOSpx3EoyMwVBFBKjsbwuBYilx92YS0/PhXqqbUumf mDEUYxSyoJixisNeZwZY1Ulp+upwMoFRz3PyFYCe+C2S58raEOCfzdRYu1iwAZk+1h3Z /VnRmDoS0b+GFodzVNvBYief/87pnJC6GQ1RV7l2gCuJPpWtFVp13l7UFlMtfproC0cj hSPWwx9ZgC2Zv1iiwxRrte0xLzDoaQ46VIK81sUW4T5FFherto/SQECl2O8xc4zJk4X+ U6/g== X-Gm-Message-State: AFq9FYLmW748v3MbiyYszKjNH2sVZDeoatfi8EJjffPYt7Vu4aT51ZJO vUprQlqlAlPo+HXGYLToox9bKQnOW3HmjM7TvH401rIPdTgYIWKc9Zco X-Gm-Gg: AYBFou3gKo3CA7h+EU0ukMeK0TUb7YeHAJ2CSDfHSruxbBpaGgxpPgMNlZ4JgYWJFD2 N0J9x1j22XC7kLfGFEdfKH4RT0d8im/8KqKsFxCHLuP7eScGDHwAF1hEhHfLit7D7Rm7EbxJ0YO VlLkRaUjIz9juagcIcWBHg6xvzvVYiH0L+Z+lRVg9E3qii5/v+vjPrVZsYPxTnCLvLLTqY9JhBh e7KcraCNrVvSKjvbjJteWLDafBA2e/lYAZaeLKbFKJWGs/prtSQJWzujTZu171ono3JtUJRzMzG uk5Gkrn192gFT3tOM9NqOONwG/IKj21CDb9UdIZPenWEsIUUtQ99o6NP+01Q2v3+a9O89OX33pv 9tlM0aLacLGIKN3VUt67S8nVBb4Zg6bCWAZxJdDWNd4ZbbpUKqStj2InbHH8Lpa5zjjuaCw1SzP APu0NMBk9gfsoPFjxIKrGenVR0DpLsqDTYDd/lHV3d0hlnukPYiOjQXbtZ12/raA4nj3Aj/+bgF ns+0j0X4UK3fUDnGW8nw6kNe6O1LpfSwA7kLZk4 X-Received: by 2002:a17:907:1c8f:b0:c26:1649:47a5 with SMTP id a640c23a62f3a-c419f121280mr341217266b.27.1792331390333; Sun, 18 Oct 2026 06:49:50 -0700 (PDT) Received: from buildhost.darklands.se ([2001:9b1:ff:d701:51eb:176f:63d9:53f8]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-c40919c0402sm267678266b.61.2026.10.18.06.49.47 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 18 Oct 2026 06:49:48 -0700 (PDT) From: Magnus Lindholm To: davem@davemloft.net, andreas@gaisler.com, sparclinux@vger.kernel.org Cc: linux-kernel@vger.kernel.org, gregkh@linuxfoundation.org, jirislaby@kernel.org, linux-serial@vger.kernel.org, linuxppc-dev@lists.ozlabs.org, krzk@kernel.org, oleg@redhat.com, linmag7@gmail.com Subject: [PATCH v4 10/10] hvc: add an M3000 firmware console backend Date: Sun, 18 Oct 2026 15:45:12 +0200 Message-ID: <20261018134738.1016106-11-linmag7@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20261018134738.1016106-1-linmag7@gmail.com> References: <20261018134738.1016106-1-linmag7@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Provide hvc0 through the M3000 Open Firmware pseudo-console, validating the firmware model and console endpoints before registering it. Keep firmware buffers in locked kernel mappings and serialize their use in atomic console contexts through the existing SPARC p1275 interface. Poll input while the TTY is open and synchronize close with timer rearming. Return zero on no progress so HVC retries buffered TTY output but drops stalled console chunks instead of spinning indefinitely. A firmware call that does not return remains unbounded. Signed-off-by: Magnus Lindholm Reviewed-by: Greg Kroah-Hartman --- MAINTAINERS | 7 ++ drivers/tty/hvc/Kconfig | 11 ++ drivers/tty/hvc/Makefile | 1 + drivers/tty/hvc/hvc_m3000.c | 202 ++++++++++++++++++++++++++++++++++++ 4 files changed, 221 insertions(+) create mode 100644 drivers/tty/hvc/hvc_m3000.c diff --git a/MAINTAINERS b/MAINTAINERS index 3a19da74d00c..21e4f4e558bd 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -10728,6 +10728,13 @@ L: platform-driver-x86@vger.kernel.org S: Maintained F: drivers/platform/x86/fujitsu-laptop.c +FUJITSU M3000 FIRMWARE CONSOLE +M: Magnus Lindholm +L: linux-serial@vger.kernel.org +L: sparclinux@vger.kernel.org +S: Maintained +F: drivers/tty/hvc/hvc_m3000.c + FUJITSU TABLET EXTRAS M: Robert Gerlach L: platform-driver-x86@vger.kernel.org diff --git a/drivers/tty/hvc/Kconfig b/drivers/tty/hvc/Kconfig index 5866195de26a..d940fa0d68e0 100644 --- a/drivers/tty/hvc/Kconfig +++ b/drivers/tty/hvc/Kconfig @@ -134,3 +134,14 @@ config HVCS will depend on arch specific APIs exported from hvcserver.ko which will also be compiled when this driver is built as a module. + +config HVC_M3000 + bool "M3000 firmware pseudo-console" + depends on SPARC64 + select HVC_DRIVER + help + Provide hvc0 using Open Firmware stdin/stdout on the Fujitsu M3000 + (IKKAKU platform). + Input is polled; no native UART registers are accessed. + Firmware calls are serialized and use a locked-image bounce buffer. + Say Y to use the M3000 firmware pseudo-console as hvc0. diff --git a/drivers/tty/hvc/Makefile b/drivers/tty/hvc/Makefile index 98880e357941..38c8cf349053 100644 --- a/drivers/tty/hvc/Makefile +++ b/drivers/tty/hvc/Makefile @@ -11,3 +11,4 @@ obj-$(CONFIG_HVC_IUCV) += hvc_iucv.o obj-$(CONFIG_HVC_UDBG) += hvc_udbg.o obj-$(CONFIG_HVC_RISCV_SBI) += hvc_riscv_sbi.o obj-$(CONFIG_HVCS) += hvcs.o +obj-$(CONFIG_HVC_M3000) += hvc_m3000.o diff --git a/drivers/tty/hvc/hvc_m3000.c b/drivers/tty/hvc/hvc_m3000.c new file mode 100644 index 000000000000..e2150d479308 --- /dev/null +++ b/drivers/tty/hvc/hvc_m3000.c @@ -0,0 +1,202 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * M3000 Open Firmware pseudo-console backend. + * Copyright (C) 2026 Magnus Lindholm + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "hvc_console.h" + +#define M3000_PROM_IO_ARGS 3 +#define M3000_PROM_IO_RETURNS 1 +#define M3000_PROM_ERROR (-1) +#define M3000_PROM_NO_DATA (-2) + +struct m3000_console { + struct timer_list poll_timer; + struct mutex poll_mutex; /* Serialize open against synchronous close. */ + raw_spinlock_t buffer_lock; + spinlock_t poll_lock; /* Protect active state and timer rearming. */ + int stdin; + u8 buffer[256]; + bool ready; + bool poll_active; +}; + +/* Keep the firmware buffer within the locked kernel image mappings. */ +static struct m3000_console m3000_consoles[] = { + { + .buffer_lock = __RAW_SPIN_LOCK_UNLOCKED(m3000_consoles[0].buffer_lock), + .poll_mutex = __MUTEX_INITIALIZER(m3000_consoles[0].poll_mutex), + .poll_lock = __SPIN_LOCK_UNLOCKED(m3000_consoles[0].poll_lock), + }, +}; + +static struct m3000_console *m3000_console(u32 termno) +{ + if (termno >= ARRAY_SIZE(m3000_consoles)) + return NULL; + return &m3000_consoles[termno]; +} + +/* + * HVC's idle backoff can exceed the firmware input FIFO's capacity. + * Wake its worker each tick while open; keep PROM calls out of the timer. + */ +static void m3000_poll_tick(struct timer_list *timer) +{ + struct m3000_console *con = timer_container_of(con, timer, poll_timer); + + guard(spinlock_irqsave)(&con->poll_lock); + if (con->poll_active) { + hvc_kick(); + mod_timer(&con->poll_timer, jiffies + 1); + } +} + +static int m3000_open(struct hvc_struct *hp, int data) +{ + struct m3000_console *con = m3000_console(hp->vtermno); + + if (!con) + return -ENODEV; + guard(mutex)(&con->poll_mutex); + guard(spinlock_irqsave)(&con->poll_lock); + con->poll_active = true; + mod_timer(&con->poll_timer, jiffies + 1); + return 0; +} + +static void m3000_close(struct hvc_struct *hp, int data) +{ + struct m3000_console *con = m3000_console(hp->vtermno); + + if (!con) + return; + guard(mutex)(&con->poll_mutex); + scoped_guard(spinlock_irqsave, &con->poll_lock) + con->poll_active = false; + /* Block reopen until the callback has finished and cannot rearm. */ + timer_delete_sync(&con->poll_timer); +} + +static ssize_t m3000_io(struct m3000_console *con, u8 *in, + const u8 *out, size_t count) +{ + unsigned long args[3 + M3000_PROM_IO_ARGS + M3000_PROM_IO_RETURNS]; + int ret; + + if (!count) + return 0; + count = min(count, in ? 1 : sizeof(con->buffer)); + /* Console writes can run with interrupts disabled; this lock cannot sleep. */ + scoped_guard(raw_spinlock_irqsave, &con->buffer_lock) { + if (!in) + memcpy(con->buffer, out, count); + args[0] = (unsigned long)(in ? "read" : "write"); + args[1] = M3000_PROM_IO_ARGS; + args[2] = M3000_PROM_IO_RETURNS; + args[3] = (unsigned int)(in ? con->stdin : prom_stdout); + args[4] = (unsigned long)con->buffer; + args[5] = count; + args[6] = (unsigned long)M3000_PROM_ERROR; + p1275_cmd_direct(args); + ret = (int)args[6]; + if (ret > 0 && ret <= count && in) + memcpy(in, con->buffer, ret); + } + if (ret == M3000_PROM_NO_DATA || ret == 0) + return 0; + if (ret < 0 || ret > count) + return -EIO; + return ret; +} + +static ssize_t m3000_get(u32 termno, u8 *buf, size_t count) +{ + struct m3000_console *con = m3000_console(termno); + + return con ? m3000_io(con, buf, NULL, count) : -ENODEV; +} + +static ssize_t m3000_put(u32 termno, const u8 *buf, size_t count) +{ + struct m3000_console *con = m3000_console(termno); + + /* HVC retries zero for TTY writes but drops stalled console output. */ + return con ? m3000_io(con, NULL, buf, count) : -ENODEV; +} + +static const struct hv_ops m3000_ops = { + .get_chars = m3000_get, + .put_chars = m3000_put, + .notifier_add = m3000_open, + .notifier_del = m3000_close, + .notifier_hangup = m3000_close, +}; + +static bool __init m3000_property_matches(phandle node, const char *prop, + const char *expected) +{ + char value[64]; + int len; + + len = prom_getproperty(node, prop, value, sizeof(value) - 1); + if (len <= 0) + return false; + value[len] = '\0'; + return !strcmp(value, expected); +} + +static int __init m3000_console_init(void) +{ + struct m3000_console *con = &m3000_consoles[0]; + phandle node; + int ret; + + if (tlb_type != sparc64_vii) + return -ENODEV; + if (!m3000_property_matches(prom_finddevice("/"), "model", "IKKAKU")) + return -ENODEV; + con->stdin = prom_getint(prom_chosen_node, "stdin"); + if (!con->stdin || con->stdin == -1 || !prom_stdout || prom_stdout == -1) + return -ENODEV; + node = prom_inst2pkg(prom_stdout); + if (!m3000_property_matches(node, "name", "pseudo-console")) + return -ENODEV; + node = prom_inst2pkg(con->stdin); + if (!m3000_property_matches(node, "name", "pseudo-console")) + return -ENODEV; + ret = hvc_instantiate(0, 0, &m3000_ops); + if (ret < 0) + return ret; + con->ready = true; + return 0; +} +console_initcall(m3000_console_init); + +static int __init m3000_tty_init(void) +{ + struct m3000_console *con = &m3000_consoles[0]; + struct hvc_struct *hp; + + if (!con->ready) + return -ENODEV; + timer_setup(&con->poll_timer, m3000_poll_tick, 0); + hp = hvc_alloc(0, 0, &m3000_ops, sizeof(con->buffer)); + if (IS_ERR(hp)) + return PTR_ERR(hp); + hp->ws.ws_row = 24; + hp->ws.ws_col = 80; + return 0; +} +device_initcall(m3000_tty_init); -- 2.43.0