From patchwork Sun Sep 27 00:20:10 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tim Michals X-Patchwork-Id: 3489 Return-Path: X-Original-To: noreply@patchwork.local Delivered-To: noreply@patchwork.local Received: from tor.lore.kernel.org (tor.lore.kernel.org [172.105.105.114]) by mxe881.netcup.net (Postfix) with ESMTPS id 794011C1D46 for ; Sun, 27 Sep 2026 02:20:56 +0200 (CEST) Authentication-Results: mxe881; dkim=pass header.d=gmail.com; spf=pass (sender IP is 172.105.105.114) smtp.mailfrom=linux-sunxi+bounces-26291-noreply=patchwork.local@lists.linux.dev smtp.helo=tor.lore.kernel.org Received-SPF: pass (mxe881: domain of lists.linux.dev designates 172.105.105.114 as permitted sender) client-ip=172.105.105.114; envelope-from=linux-sunxi+bounces-26291-noreply=patchwork.local@lists.linux.dev; helo=tor.lore.kernel.org; Received: from smtp.subspace.kernel.org (conduit.subspace.kernel.org [100.90.174.1]) by tor.lore.kernel.org (Postfix) with ESMTP id C588D292EF for ; Sun, 27 Sep 2026 00:20:51 +0000 (UTC) Received: from localhost.localdomain (localhost.localdomain [127.0.0.1]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 8262922538F; Sun, 27 Sep 2026 00:20:51 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="KB+Ok63j" X-Original-To: linux-sunxi@lists.linux.dev Received: from mail-oo2-f39.google.com (mail-oo2-f39.google.com [74.125.231.167]) (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 BF0CD23F40D for ; Sun, 27 Sep 2026 00:20:49 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.231.167 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790468451; cv=none; b=b/kvLImvkL3nTzqaeZbchU1oWMW38guNK9JHiZvADMfxL0tlFuKouKrI2Sx9wZCmhSf+uAX+Agj7kjRyHJ3nJwIQ3yYEGspL/FGCRo3nvEMjaBkMEEW11msiXM9mlsWUVb1vav22aeH/yuvKqHNV5rRb0ynhB7u6YbevxzlxWt8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790468451; c=relaxed/simple; bh=KUqMhiQcS4AgsYPuncz05+mw8gLtazXZDPzDeOszHBs=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=PEhj+L4MAnzTJUc5XV85+KwzT+7on522gDhfdKLv14fI4DvNHLrtbJoK0BF0MM0hwmHnIs5C4NKnz/F7ryNVUHtb8j0vJV0XrMt6PTphpFP8sTegrXE0yZf7sGUKcQnJLjUi/JsO20LdhSzoXZFZj/IkdecErOa1DC+d8Lv6cpY= 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=KB+Ok63j; arc=none smtp.client-ip=74.125.231.167 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 Received: by mail-oo2-f39.google.com with SMTP id 46e09a7af769-7f4f0cfb33cso1562285a34.0 for ; Sat, 26 Sep 2026 17:20:49 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1790468448; x=1791073248; darn=lists.linux.dev; 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=B5J3pzpTC8euWZ64R9GaI6gZQCXcNFJ4o9q8CcjL5ik=; b=KB+Ok63jy7y2mGEol08MfjoMp0CKsqnur4T0MrtbNDzgNYnSYIvf8zbZjxCUWllsDH 0bwGTPM2vI86wik4zegrXkAIRN5d5RwqDxyulb4jSi+K9nEOGWVad5plCJuaxnBG0gqP 1VOoUO74WlWfgoaRe+JuUPUYY+lgovENsYJRanhxZ+n/Gr/LhyT+2i4PalIyx9Bweqq2 63HYb69MElHo98mLoUGyMgPERdfPKW4YVzpfO2ESzftKYrNkyNrsQ0GNHvFaTNpgtEXe jcoiTNrOUhAv1YI08XTBFDjHGOqilUPuYPYjN2/t7nZRKJGkavI/VmGfWTiT9ml63ClO +jyw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1790468448; x=1791073248; 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=B5J3pzpTC8euWZ64R9GaI6gZQCXcNFJ4o9q8CcjL5ik=; b=oM9IBsQvKMX2fSxWKuZ6zVOqR02tEotisOALZ4HePQNA3zrcarfAvVLGPEQ+ScTJWl 6CdV0G4AGlLFozHnKT6NbBDF25vZ8qJI4gF+LRGLIpsqGZJyGDDzeGN0l6qF8h1ajFen 2LHxoUODz0lPOxP6BIxxiFrC9Ugp2HAMEZbfB8U04/A4tc5nrjcpyiTmsN4fWGWA5sE5 eVanAMERu9KgXwvfWyAWrtzZPf9+UtLuQu8AaP8NbBIB2+kASVaeuwOVPUtAgGqkYcvY VlR95Mva/N92xqtp7f6O4OfeCF4uaOI7gjx6iyL6rx1fl5QZJIckBexDKqotXUU/jmeD xC3A== X-Gm-Message-State: AFuF++mIRMhEf7NPULKac8buqynPSdST5A6oAspuFTQmjITrec5RaZou na0vCr5od/4pn0URZyexMxe8igaz9Dfk9TBkarkBT3XmPj3IweAHU0X3tnPL1J7s X-Gm-Gg: AYBFou0h/zMn2A1GkAwCzljzKO3SJR5Q/x6+M7NcE4mXXWBiU57q/6agEsrPVOHI30C PaQEgDjy/Ae0g9mDBL7B5Mg3OYD5LbxXBuCjum6DFFYxnn9jdcgMmCkh+EbSpyLmMpI2HQmyzWA kk13y8jwh5nI1/6oAq5Ih+CEqC83w3UnaSCXmPWfDcIsCvd0AXsT6RTUzkJsz9SnsAfDBFYkXK5 7FvrAxZhkmrt89aGqNtmKfVBpVjjSbb/Bx79avuLu9Amnw6L5Kw+OEDCf6DtpwNlc7XSGZsPrWe xAUZOsi0tfSfwyGf/0FlayUpDXN/Hv5x/Oj/eGvQLJgzsSy2tNH5GgzOJmCPtt8Hw2XxKcjKW4e T1qxEKvlu34/Zxc2Lfc+5eU9ueeHvXaLRcdX+eQEeCvVs0glSwoUht3yQxH3VXzNO+8MCAYFPi9 /pXiKITsPaYW/nFNeIZ+NQmuVx8CotMbpJ7HjkQ5inqvWo4FkYMiyZgLal9ACbaTsG+2MzkZBfW z+aAycyoVXGjgl1ATIzuA== X-Received: by 2002:a05:6820:290e:b0:6d8:6dcf:54c7 with SMTP id 006d021491bc7-6d86dcf582fmr353279eaf.82.1790468448578; Sat, 26 Sep 2026 17:20:48 -0700 (PDT) Received: from tcmichals-5759.lan ([2607:fb91:3584:35a9:66f8:2cb5:aa80:21fd]) by smtp.gmail.com with ESMTPSA id 006d021491bc7-6d8828e1ea0sm838989eaf.6.2026.09.26.17.20.46 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 26 Sep 2026 17:20:47 -0700 (PDT) From: Tim Michals To: linux-sunxi@lists.linux.dev, linux-remoteproc@vger.kernel.org Cc: devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, wens@kernel.org, jernej.skrabec@gmail.com, samuel@sholland.org, andersson@kernel.org, mathieu.poirier@linaro.org, jassisinghbrar@gmail.com, robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org, Tim Michals Subject: [PATCH v2 1/7] dt-bindings: mailbox: add Allwinner sun55i msgbox schema Date: Sat, 26 Sep 2026 19:20:10 -0500 Message-ID: <20260927002021.797069-2-tcmichals@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260927002021.797069-1-tcmichals@gmail.com> References: <20260922034711.190253-1-tcmichals@gmail.com> <20260927002021.797069-1-tcmichals@gmail.com> Precedence: bulk X-Mailing-List: linux-sunxi@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Rspamd-Server: rspamd-worker-8404 X-Spamd-Result: default: False [0.34 / 15.00]; BAYES_HAM(-5.50)[100.00%]; RBL_SENDERSCORE(2.00)[172.105.105.114:from]; SUSPICIOUS_RECIPS(1.50)[]; MID_CONTAINS_FROM(1.00)[]; DMARC_POLICY_SOFTFAIL(1.00)[gmail.com : SPF not aligned (relaxed), No valid DKIM,none]; R_MISSING_CHARSET(0.50)[]; MAILLIST(-0.15)[generic]; BAD_REP_POLICIES(0.10)[]; MIME_GOOD(-0.10)[text/plain]; HAS_LIST_UNSUB(-0.01)[]; RCPT_COUNT_TWELVE(0.00)[15]; DBL_BLOCKED_OPENRESOLVER(0.00)[devicetree.org:url,tor.lore.kernel.org:rdns,tor.lore.kernel.org:helo,0.45.210.120:email]; TO_DN_SOME(0.00)[]; FREEMAIL_FROM(0.00)[gmail.com]; FORGED_SENDER_MAILLIST(0.00)[]; FREEMAIL_CC(0.00)[vger.kernel.org,lists.infradead.org,kernel.org,gmail.com,sholland.org,linaro.org]; TAGGED_RCPT(0.00)[dt]; PRECEDENCE_BULK(0.00)[]; FROM_HAS_DN(0.00)[]; RCVD_TLS_LAST(0.00)[]; MIME_TRACE(0.00)[0:+]; FORGED_RECIPIENTS_MAILLIST(0.00)[]; R_SPF_ALLOW(0.00)[+ip4:172.105.105.114]; ASN(0.00)[asn:63949, ipnet:172.105.96.0/20, country:SG]; ARC_ALLOW(0.00)[subspace.kernel.org:s=arc-20240116:i=1]; TAGGED_FROM(0.00)[bounces-26291-noreply=patchwork.local]; RCVD_COUNT_FIVE(0.00)[6]; FROM_NEQ_ENVFROM(0.00)[tcmichals@gmail.com,linux-sunxi@lists.linux.dev]; RCVD_VIA_SMTP_AUTH(0.00)[] X-Rspamd-Queue-Id: 794011C1D46 X-MORS-Enabled: yes X-MORS-DOMAIN: patchwork.local X-MORS-HOSTING: hosting172546 X-MORS-USER: hosting172546 X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= Add Device Tree binding schema for the Allwinner 4-port hardware Message Box controller found on sun55i (A523, A527, T527) and sun60i (A733) SoCs. The message box connects the ARM Cortex-A55 host cluster to the HiFi4 Audio DSP, Power Management Unit (CPUS), and XuanTie RISC-V co-processor across 12 logical channels with 8-entry hardware FIFOs. Signed-off-by: Tim Michals --- .../mailbox/allwinner,sun55i-a523-msgbox.yaml | 102 ++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 Documentation/devicetree/bindings/mailbox/allwinner,sun55i-a523-msgbox.yaml diff --git a/Documentation/devicetree/bindings/mailbox/allwinner,sun55i-a523-msgbox.yaml b/Documentation/devicetree/bindings/mailbox/allwinner,sun55i-a523-msgbox.yaml new file mode 100644 index 000000000000..6b0c7ff70c9f --- /dev/null +++ b/Documentation/devicetree/bindings/mailbox/allwinner,sun55i-a523-msgbox.yaml @@ -0,0 +1,102 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/mailbox/allwinner,sun55i-a523-msgbox.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Allwinner sun55i 4-Port Message Box + +maintainers: + - Tim Michals + - Samuel Holland + +description: + The hardware message box on sun55i (A523, A527, T527) and sun60i (A733) + SoCs is a 4-port multi-processor mailbox controller connecting the ARM + Cortex-A55 host cluster, HiFi4 Audio DSP, Power Management Unit (CPUS), + and XuanTie RISC-V co-processor. + + It features independent 256-byte register banks for each processor port, + 12 logical channels on the host (4 per remote processor), and 8-entry + hardware FIFOs with level interrupts. + +properties: + compatible: + enum: + - allwinner,sun55i-a523-msgbox + - allwinner,sun55i-t527-msgbox + - allwinner,sun60i-a733-msgbox + + reg: + maxItems: 4 + description: + Register banks for each of the four processor ports. Each port has + an independent 256-byte register bank. + + reg-names: + items: + - const: arm + - const: dsp + - const: cpus + - const: rv + + clocks: + maxItems: 1 + + resets: + maxItems: 1 + + interrupts: + minItems: 1 + maxItems: 4 + description: + One interrupt per processor port in port order (arm, dsp, cpus, rv). + The ARM host port interrupt is required; remote port interrupts are + optional. Use interrupt-names to identify which ports are present when + providing a partial list. + + interrupt-names: + minItems: 1 + maxItems: 4 + items: + enum: + - arm + - dsp + - cpus + - rv + + '#mbox-cells': + const: 1 + description: "channel number (0-11: 0-3 CPUS, 4-7 DSP, 8-11 RISC-V)" + +required: + - compatible + - reg + - reg-names + - clocks + - resets + - interrupts + - '#mbox-cells' + +additionalProperties: false + +examples: + - | + #include + + mailbox@3003000 { + compatible = "allwinner,sun55i-a523-msgbox"; + reg = <0x03003000 0x1000>, + <0x07094000 0x1000>, + <0x07120000 0x1000>, + <0x07136000 0x1000>; + reg-names = "arm", "dsp", "cpus", "rv"; + clocks = <&ccu 120>; + resets = <&ccu 45>; + interrupts = , + , + , + ; + interrupt-names = "arm", "dsp", "cpus", "rv"; + #mbox-cells = <1>; + }; From patchwork Sun Sep 27 00:20:11 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tim Michals X-Patchwork-Id: 3490 Return-Path: X-Original-To: noreply@patchwork.local Delivered-To: noreply@patchwork.local Received: from sea.lore.kernel.org (sea.lore.kernel.org [172.234.253.10]) by mxe881.netcup.net (Postfix) with ESMTPS id 4EE491C1D46 for ; Sun, 27 Sep 2026 02:21:42 +0200 (CEST) Authentication-Results: mxe881; dkim=pass header.d=gmail.com; spf=pass (sender IP is 172.234.253.10) smtp.mailfrom=linux-sunxi+bounces-26292-noreply=patchwork.local@lists.linux.dev smtp.helo=sea.lore.kernel.org Received-SPF: pass (mxe881: domain of lists.linux.dev designates 172.234.253.10 as permitted sender) client-ip=172.234.253.10; envelope-from=linux-sunxi+bounces-26292-noreply=patchwork.local@lists.linux.dev; helo=sea.lore.kernel.org; Received: from smtp.subspace.kernel.org (conduit.subspace.kernel.org [100.90.174.1]) by sea.lore.kernel.org (Postfix) with ESMTP id 073C633336 for ; Sun, 27 Sep 2026 00:20:56 +0000 (UTC) Received: from localhost.localdomain (localhost.localdomain [127.0.0.1]) by smtp.subspace.kernel.org (Postfix) with ESMTP id D612823F40D; Sun, 27 Sep 2026 00:20:55 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="G3uhclwG" X-Original-To: linux-sunxi@lists.linux.dev Received: from mail-oo2-f37.google.com (mail-oo2-f37.google.com [74.125.231.165]) (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 3B470242D70 for ; Sun, 27 Sep 2026 00:20:52 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.231.165 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790468455; cv=none; b=k25k6JGfZur4c+q2PCgZH0IjyINfDRlzQ2vx2F4+9zXD1YVOdLupN2gjflymA0uhvPQa1LC70Q26YK+svVTGgkBbFcuezdLKkPc3Oy/IuNqXC9cI+tiBUAj9IkcYUKybSTifgK44LtdwJqpHHsdGpDKYC8VDnYCdrVTDAbRnZlQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790468455; c=relaxed/simple; bh=GIeFzqkf+t90OFEOly3kIqkEDK9YoFqQscjmy9Bb7zM=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Oigjb8oynfwYiVnbMD85UlbnGdn8n80tgHr6PvJYobYDbi3qGmiivRqP1vH0N0ZTDUyOer0KSGqYKXjaxVmh/qhNMF9MlLvtIld/OY72y+LYV7YNeGobIRV5+YZom4wHLiVflNQGk+xVszkmWJTimAQ4fGFLUuEdKXKx2Hrsj6Q= 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=G3uhclwG; arc=none smtp.client-ip=74.125.231.165 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 Received: by mail-oo2-f37.google.com with SMTP id 46e09a7af769-80032c08611so1594482a34.3 for ; Sat, 26 Sep 2026 17:20:52 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1790468451; x=1791073251; darn=lists.linux.dev; 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=hI4LzcR5N9hwvq4XXdv1TVoYIFqgkaE3x77LOlHbgrY=; b=G3uhclwGxWoyN80LvPbOfg9qECtlOAAeFX4f4CxtGs6jRify2qDhpFrT2Aim7iQBaE 2mnj83KnTcVvjay8FqOMpAGTaIyj6vVTngQuRO5/Ne54X/6y60A/Bkbvn9LkXouEKfYl lfxXvbL9GnNuFkGtiT2nDi0o91G7sO0BkQcz7k4zFhyui1aKc0jtnAZ3wZdfyK9mzKel BCvHDtUE6AGkuG2UeyTc6RRMzhaQuffxXNBDMeY6AwRQ9tOphgSI1TW8O2eLFQDXfdIk sToBE1YnnibxCeoIf14vKSAyx+ATUvygeaDtON+rYqbrY+QAHo8hgnm0u5rwTCOaBhm7 jwBw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1790468451; x=1791073251; 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=hI4LzcR5N9hwvq4XXdv1TVoYIFqgkaE3x77LOlHbgrY=; b=JVXxq/4fPweyL4wY7phBxPgDV0GTIxufiawVfHn6d42SAu7kBdBQ1fw+jL6wxkQ0pE gTPmSdD0iwJEnHaNRXVO5oqrwWpUEzJ1Gop/0O2TDu6HlfDes6x/4k5uUKQAP14gH75k SsNuT7SKkyHFyXJzjKOmHWSZ4O2bRSaFtvVDaHdbY9daThOBJNp9J3Hfem6PD32bgLkS y/YWteqfQpL2SeP1H3bcz7RWKVD3iQa3AVEqDfnxU1cxfA3G0BelN3+SCIFi0vcrpdOQ nLiEgU1Dzx0cBXRWhM3SgCzG8WAtUxdIprDNTNwnaVWrT7/KLm1+QDuDxg7tGJJB7aKK K1kQ== X-Gm-Message-State: AFuF++mEDB6hRy1/EtI16hglHtR8hoWC2Oi4rAfvvG8caDn2B9+AMHjY JaHW04C2/dchzGc1fNAr7DiAEzwWcDse7U0se5Tr0WkIYX8PIIlyH+0r3iJ03+5B X-Gm-Gg: AYBFou1Xg42arkVB2/4pWfIpJ65pSYdiI6S5Xao9WVCufEOPdmQ4G645KUEqZWhem1m wb9ToaAjWm1yGwJCQhRYpLjYl2O7XG40YJEoyVKMqZGYvZNY+6O1nfOu7C2CkcWz1N2BrKsSXUh Sq9Z3t4SUaWGDM6aSUMKhaNYjZHUjXZoc1v6ImyQ9fPROz5ssgH4G4oFGS2lR88Px3gnf9AlK1g eNBCdxDgskLfK/PVJf034rPSj/2/HMi0LB/q4Apsjdp7jEPkWISjLo5dfSpjdE2bFcAbhWvg7Ye /bYBCjtLiIAyIr25Vv5Ny5OJFxbxvZ15hkOY4tHOd0QvW1AF55qqNhf6ONQi97veO74987OFaHR TOAm5QkDl0dS8UHYzywZ8VVKF4heFgpLXZjC7U2F/DRkVylFwyEWBnilSJeO5HMpTajwUQL7ddO Blfyf8qJDpJgsXSgD97GgKWbIp9kbRGPYWss4QWBwpoFiURwJJrzNiRqMQPwn1gULkHLxx4TvK7 Mr6M/SJhP9xGLbR3UV6Ww== X-Received: by 2002:a05:6820:3107:b0:6c9:80d8:a1e7 with SMTP id 006d021491bc7-6d43fd7af20mr9804934eaf.36.1790468451131; Sat, 26 Sep 2026 17:20:51 -0700 (PDT) Received: from tcmichals-5759.lan ([2607:fb91:3584:35a9:66f8:2cb5:aa80:21fd]) by smtp.gmail.com with ESMTPSA id 006d021491bc7-6d8828e1ea0sm838989eaf.6.2026.09.26.17.20.48 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 26 Sep 2026 17:20:50 -0700 (PDT) From: Tim Michals To: linux-sunxi@lists.linux.dev, linux-remoteproc@vger.kernel.org Cc: devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, wens@kernel.org, jernej.skrabec@gmail.com, samuel@sholland.org, andersson@kernel.org, mathieu.poirier@linaro.org, jassisinghbrar@gmail.com, robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org, Tim Michals Subject: [PATCH v2 2/7] mailbox: sun55i: add Allwinner sun55i/sun60i 4-port Message Box driver Date: Sat, 26 Sep 2026 19:20:11 -0500 Message-ID: <20260927002021.797069-3-tcmichals@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260927002021.797069-1-tcmichals@gmail.com> References: <20260922034711.190253-1-tcmichals@gmail.com> <20260927002021.797069-1-tcmichals@gmail.com> Precedence: bulk X-Mailing-List: linux-sunxi@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-MORS-Enabled: yes X-MORS-DOMAIN: patchwork.local X-MORS-HOSTING: hosting172546 X-MORS-USER: hosting172546 X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= Add driver for the 4-port hardware Message Box controller found on Allwinner A523, A527, T527 (sun55i) and A733 (sun60i) SoCs. The controller acts as a hardware crossbar connecting four heterogeneous processor cores (ARM Cortex-A55, Tensilica HiFi4 DSP, CPUS, and XuanTie E907 RISC-V) across 12 logical channels with 8-entry hardware message FIFOs. Signed-off-by: Tim Michals --- drivers/mailbox/Kconfig | 10 + drivers/mailbox/Makefile | 1 + drivers/mailbox/sun55i-msgbox.c | 375 ++++++++++++++++++++++++++++++++ drivers/mailbox/sun55i-msgbox.h | 58 +++++ 4 files changed, 444 insertions(+) create mode 100644 drivers/mailbox/sun55i-msgbox.c create mode 100644 drivers/mailbox/sun55i-msgbox.h diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig index ab869769e278..9067f5161276 100644 --- a/drivers/mailbox/Kconfig +++ b/drivers/mailbox/Kconfig @@ -332,6 +332,16 @@ config SUN6I_MSGBOX various Allwinner SoCs. This mailbox is used for communication between the application CPUs and the power management coprocessor. +config SUN55I_MSGBOX + tristate "Allwinner sun55i/sun60i 4-port Message Box" + depends on ARCH_SUNXI || COMPILE_TEST + default ARCH_SUNXI + help + Mailbox implementation for the 4-port hardware message box present in + Allwinner A523/A527/T527 (sun55i) and A733 (sun60i) SoCs. This mailbox + provides 12 hardware FIFO channels for communication between the + Cortex-A55 host, CPUS, HiFi4 DSP, and XuanTie E907 RISC-V coprocessors. + config SPRD_MBOX tristate "Spreadtrum Mailbox" depends on ARCH_SPRD || COMPILE_TEST diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile index 45dc59bb3e7f..aa17f9d0acd4 100644 --- a/drivers/mailbox/Makefile +++ b/drivers/mailbox/Makefile @@ -70,6 +70,7 @@ obj-$(CONFIG_MTK_VCP_MBOX) += mtk-vcp-mailbox.o obj-$(CONFIG_ZYNQMP_IPI_MBOX) += zynqmp-ipi-mailbox.o obj-$(CONFIG_SUN6I_MSGBOX) += sun6i-msgbox.o +obj-$(CONFIG_SUN55I_MSGBOX) += sun55i-msgbox.o obj-$(CONFIG_SPRD_MBOX) += sprd-mailbox.o diff --git a/drivers/mailbox/sun55i-msgbox.c b/drivers/mailbox/sun55i-msgbox.c new file mode 100644 index 000000000000..928e52e08bad --- /dev/null +++ b/drivers/mailbox/sun55i-msgbox.c @@ -0,0 +1,375 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Allwinner sun55i/sun60i 4-Port Hardware Message Box Driver + * + * Copyright (C) 2026 Tim Michals + * Based on vendor sunxi-msgbox driver by Allwinner Technology Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sun55i-msgbox.h" + +/* + * Hardware routing table for Cortex-A55 host (local_id = 0): + * local_n = 0 -> CPUS (remote_id = SUN55I_PROC_CPUS, remote_n = 0) -> Channels 0..3 + * local_n = 1 -> DSP (remote_id = SUN55I_PROC_DSP, remote_n = 0) -> Channels 4..7 + * local_n = 2 -> RV (remote_id = SUN55I_PROC_RV, remote_n = 2) -> Channels 8..11 + */ +const struct sun55i_route sun55i_msgbox_arm_routes[SUN55I_NUM_ROUTES] = { + [0] = { .remote_id = SUN55I_PROC_CPUS, .remote_n = 0 }, + [1] = { .remote_id = SUN55I_PROC_DSP, .remote_n = 0 }, + [2] = { .remote_id = SUN55I_PROC_RV, .remote_n = 2 }, +}; + +#if IS_ENABLED(CONFIG_SUN55I_MSGBOX_KUNIT_TEST) +EXPORT_SYMBOL_GPL(sun55i_msgbox_arm_routes); +#endif + +static inline struct sun55i_msgbox *to_sun55i_msgbox(struct mbox_chan *chan) +{ + return chan->con_priv; +} + +void sun55i_chan_to_route(int chan_idx, int *local_n, int *p, + int *remote_id, int *remote_n) +{ + if (chan_idx < 0 || chan_idx >= SUN55I_NUM_CHANS) { + *local_n = 0; + *p = 0; + *remote_id = 0; + *remote_n = 0; + return; + } + *local_n = chan_idx / SUN55I_CHANS_PER_PROC; + *p = chan_idx % SUN55I_CHANS_PER_PROC; + *remote_id = sun55i_msgbox_arm_routes[*local_n].remote_id; + *remote_n = sun55i_msgbox_arm_routes[*local_n].remote_n; +} + +#if IS_ENABLED(CONFIG_SUN55I_MSGBOX_KUNIT_TEST) +EXPORT_SYMBOL_GPL(sun55i_chan_to_route); +#endif + +irqreturn_t sun55i_msgbox_irq(int irq, void *dev_id) +{ + struct sun55i_msgbox *mbox = dev_id; + irqreturn_t ret = IRQ_NONE; + int i, local_n, p, chan_idx; + + for (local_n = 0; local_n < SUN55I_NUM_ROUTES; local_n++) { + void __iomem *local_base = mbox->regs[0]; + u32 en, stat, pending; + + en = readl(local_base + SUNXI_MSGBOX_READ_IRQ_ENABLE(local_n)); + stat = readl(local_base + SUNXI_MSGBOX_READ_IRQ_STATUS(local_n)); + pending = en & stat; + + if (!pending) + continue; + + for (p = 0; p < SUN55I_CHANS_PER_PROC; p++) { + if (!(pending & RD_IRQ_PEND_BIT(p))) + continue; + + chan_idx = local_n * SUN55I_CHANS_PER_PROC + p; + + /* + * Clear pending status BEFORE draining the FIFO to seal + * the TOCTOU race: any new message arriving while draining + * will re-assert the hardware pending bit, guaranteeing + * a subsequent interrupt and preventing blackholed IPC data. + */ + writel(RD_IRQ_PEND_BIT(p), + local_base + SUNXI_MSGBOX_READ_IRQ_STATUS(local_n)); + + /* Cap drain at FIFO_MAX to prevent CPU lockup from a runaway remote */ + for (i = 0; i < SUN55I_FIFO_MAX; i++) { + u32 msg; + + if (!(readl(local_base + + SUNXI_MSGBOX_MSG_STATUS(local_n, p)) & MSG_NUM_MASK)) + break; + msg = readl(local_base + SUNXI_MSGBOX_MSG_FIFO(local_n, p)); + mbox_chan_received_data(&mbox->controller.chans[chan_idx], &msg); + } + + ret = IRQ_HANDLED; + } + } + + return ret; +} + +#if IS_ENABLED(CONFIG_SUN55I_MSGBOX_KUNIT_TEST) +EXPORT_SYMBOL_GPL(sun55i_msgbox_irq); +#endif + +static int sun55i_msgbox_send_data(struct mbox_chan *chan, void *data) +{ + struct sun55i_msgbox *mbox = to_sun55i_msgbox(chan); + int n = chan - mbox->controller.chans; + int local_n, p, remote_id, remote_n; + u32 msg = 0; + + if (data) + memcpy(&msg, data, sizeof(msg)); + + sun55i_chan_to_route(n, &local_n, &p, &remote_id, &remote_n); + + writel(msg, mbox->regs[remote_id] + SUNXI_MSGBOX_MSG_FIFO(remote_n, p)); + return 0; +} + +static int sun55i_msgbox_startup(struct mbox_chan *chan) +{ + struct sun55i_msgbox *mbox = to_sun55i_msgbox(chan); + int n = chan - mbox->controller.chans; + int i, local_n, p, remote_id, remote_n; + unsigned long flags; + u32 val; + + sun55i_chan_to_route(n, &local_n, &p, &remote_id, &remote_n); + + /* Flush any stale receive data (bounded to FIFO_MAX) */ + for (i = 0; i < SUN55I_FIFO_MAX; i++) { + if (!(readl(mbox->regs[0] + SUNXI_MSGBOX_MSG_STATUS(local_n, p)) & MSG_NUM_MASK)) + break; + readl(mbox->regs[0] + SUNXI_MSGBOX_MSG_FIFO(local_n, p)); + } + + /* Clear pending status */ + writel(RD_IRQ_PEND_BIT(p), + mbox->regs[0] + SUNXI_MSGBOX_READ_IRQ_STATUS(local_n)); + + /* Enable receive IRQ */ + spin_lock_irqsave(&mbox->lock, flags); + val = readl(mbox->regs[0] + SUNXI_MSGBOX_READ_IRQ_ENABLE(local_n)); + val |= RD_IRQ_EN_BIT(p); + writel(val, mbox->regs[0] + SUNXI_MSGBOX_READ_IRQ_ENABLE(local_n)); + spin_unlock_irqrestore(&mbox->lock, flags); + + return 0; +} + +static void sun55i_msgbox_shutdown(struct mbox_chan *chan) +{ + struct sun55i_msgbox *mbox = to_sun55i_msgbox(chan); + int n = chan - mbox->controller.chans; + int i, local_n, p, remote_id, remote_n; + unsigned long flags; + u32 val; + + sun55i_chan_to_route(n, &local_n, &p, &remote_id, &remote_n); + + /* Disable receive IRQ */ + spin_lock_irqsave(&mbox->lock, flags); + val = readl(mbox->regs[0] + SUNXI_MSGBOX_READ_IRQ_ENABLE(local_n)); + val &= ~RD_IRQ_EN_BIT(p); + writel(val, mbox->regs[0] + SUNXI_MSGBOX_READ_IRQ_ENABLE(local_n)); + spin_unlock_irqrestore(&mbox->lock, flags); + + /* Clear pending status and flush (bounded to FIFO_MAX) */ + writel(RD_IRQ_PEND_BIT(p), + mbox->regs[0] + SUNXI_MSGBOX_READ_IRQ_STATUS(local_n)); + for (i = 0; i < SUN55I_FIFO_MAX; i++) { + if (!(readl(mbox->regs[0] + SUNXI_MSGBOX_MSG_STATUS(local_n, p)) & MSG_NUM_MASK)) + break; + readl(mbox->regs[0] + SUNXI_MSGBOX_MSG_FIFO(local_n, p)); + } +} + +static bool sun55i_msgbox_last_tx_done(struct mbox_chan *chan) +{ + struct sun55i_msgbox *mbox = to_sun55i_msgbox(chan); + int n = chan - mbox->controller.chans; + int local_n, p, remote_id, remote_n; + u32 count; + + sun55i_chan_to_route(n, &local_n, &p, &remote_id, &remote_n); + + count = readl(mbox->regs[remote_id] + SUNXI_MSGBOX_MSG_STATUS(remote_n, p)) & MSG_NUM_MASK; + return count < SUN55I_FIFO_MAX; +} + +static bool sun55i_msgbox_peek_data(struct mbox_chan *chan) +{ + struct sun55i_msgbox *mbox = to_sun55i_msgbox(chan); + int n = chan - mbox->controller.chans; + int local_n, p, remote_id, remote_n; + u32 count; + + sun55i_chan_to_route(n, &local_n, &p, &remote_id, &remote_n); + + count = readl(mbox->regs[0] + SUNXI_MSGBOX_MSG_STATUS(local_n, p)) & MSG_NUM_MASK; + return count > 0; +} + +const struct mbox_chan_ops sun55i_msgbox_chan_ops = { + .send_data = sun55i_msgbox_send_data, + .startup = sun55i_msgbox_startup, + .shutdown = sun55i_msgbox_shutdown, + .last_tx_done = sun55i_msgbox_last_tx_done, + .peek_data = sun55i_msgbox_peek_data, +}; + +#if IS_ENABLED(CONFIG_SUN55I_MSGBOX_KUNIT_TEST) +EXPORT_SYMBOL_GPL(sun55i_msgbox_chan_ops); +#endif + +static int sun55i_msgbox_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct mbox_chan *chans; + struct sun55i_msgbox *mbox; + int i, ret, irq_cnt, local_n; + + mbox = devm_kzalloc(dev, sizeof(*mbox), GFP_KERNEL); + if (!mbox) + return -ENOMEM; + + spin_lock_init(&mbox->lock); + + /* Allocate channels early before touching any hardware */ + chans = devm_kcalloc(dev, SUN55I_NUM_CHANS, sizeof(*chans), GFP_KERNEL); + if (!chans) + return -ENOMEM; + + for (i = 0; i < SUN55I_NUM_CHANS; i++) + chans[i].con_priv = mbox; + + for (i = 0; i < SUN55I_MAX_PROCESSORS; i++) { + mbox->regs[i] = devm_platform_ioremap_resource(pdev, i); + if (IS_ERR(mbox->regs[i])) + return dev_err_probe(dev, PTR_ERR(mbox->regs[i]), + "failed to map resource %d\n", i); + } + + mbox->clk = devm_clk_get(dev, NULL); + if (IS_ERR(mbox->clk)) + return dev_err_probe(dev, PTR_ERR(mbox->clk), "failed to get clock\n"); + + ret = clk_prepare_enable(mbox->clk); + if (ret) + return dev_err_probe(dev, ret, "failed to enable clock\n"); + + mbox->reset = devm_reset_control_get_optional_shared(dev, NULL); + if (IS_ERR(mbox->reset)) { + ret = PTR_ERR(mbox->reset); + goto err_disable_clk; + } + + ret = reset_control_deassert(mbox->reset); + if (ret) + goto err_disable_clk; + + /* Disable all read IRQs and clear status */ + for (local_n = 0; local_n < SUN55I_NUM_ROUTES; local_n++) { + writel(0, mbox->regs[0] + SUNXI_MSGBOX_READ_IRQ_ENABLE(local_n)); + writel(U32_MAX, mbox->regs[0] + SUNXI_MSGBOX_READ_IRQ_STATUS(local_n)); + } + + irq_cnt = platform_irq_count(pdev); + if (irq_cnt < 0) { + ret = irq_cnt; + goto err_assert_reset; + } + + for (i = 0; i < irq_cnt; i++) { + int irq = platform_get_irq(pdev, i); + + if (irq < 0) { + ret = irq; + goto err_free_irqs; + } + + ret = request_irq(irq, sun55i_msgbox_irq, + IRQF_SHARED, dev_name(dev), mbox); + if (ret) { + dev_err(dev, "failed to request irq %d: %d\n", irq, ret); + goto err_free_irqs; + } + mbox->irqs[i] = irq; + mbox->num_irqs = i + 1; + } + + mbox->controller.dev = dev; + mbox->controller.ops = &sun55i_msgbox_chan_ops; + mbox->controller.chans = chans; + mbox->controller.num_chans = SUN55I_NUM_CHANS; + mbox->controller.txdone_irq = false; + mbox->controller.txdone_poll = true; + mbox->controller.txpoll_period = 1; + + platform_set_drvdata(pdev, mbox); + + ret = mbox_controller_register(&mbox->controller); + if (ret) { + dev_err_probe(dev, ret, "failed to register controller\n"); + goto err_free_irqs; + } + + return 0; + +err_free_irqs: + /* Mask all hardware read IRQs and free registered IRQs before cutting clocks */ + for (local_n = 0; local_n < SUN55I_NUM_ROUTES; local_n++) + writel(0, mbox->regs[0] + SUNXI_MSGBOX_READ_IRQ_ENABLE(local_n)); + for (i = 0; i < mbox->num_irqs; i++) + free_irq(mbox->irqs[i], mbox); +err_assert_reset: + reset_control_assert(mbox->reset); +err_disable_clk: + clk_disable_unprepare(mbox->clk); + return ret; +} + +static void sun55i_msgbox_remove(struct platform_device *pdev) +{ + struct sun55i_msgbox *mbox = platform_get_drvdata(pdev); + int local_n, i; + + mbox_controller_unregister(&mbox->controller); + + /* Mask hardware interrupts and free IRQs before asserting reset and disabling clock */ + for (local_n = 0; local_n < SUN55I_NUM_ROUTES; local_n++) + writel(0, mbox->regs[0] + SUNXI_MSGBOX_READ_IRQ_ENABLE(local_n)); + + for (i = 0; i < mbox->num_irqs; i++) + free_irq(mbox->irqs[i], mbox); + + reset_control_assert(mbox->reset); + clk_disable_unprepare(mbox->clk); +} + +static const struct of_device_id sun55i_msgbox_of_match[] = { + { .compatible = "allwinner,sun55i-a523-msgbox" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, sun55i_msgbox_of_match); + +static struct platform_driver sun55i_msgbox_driver = { + .driver = { + .name = "sun55i-msgbox", + .of_match_table = sun55i_msgbox_of_match, + }, + .probe = sun55i_msgbox_probe, + .remove = sun55i_msgbox_remove, +}; +module_platform_driver(sun55i_msgbox_driver); + +MODULE_AUTHOR("Tim Michals "); +MODULE_DESCRIPTION("Allwinner sun55i/sun60i 4-Port Message Box Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mailbox/sun55i-msgbox.h b/drivers/mailbox/sun55i-msgbox.h new file mode 100644 index 000000000000..2ab79a9149c0 --- /dev/null +++ b/drivers/mailbox/sun55i-msgbox.h @@ -0,0 +1,58 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _SUN55I_MSGBOX_H_ +#define _SUN55I_MSGBOX_H_ + +#include +#include +#include +#include +#include + +#define SUN55I_PROC_ARM 0 +#define SUN55I_PROC_DSP 1 +#define SUN55I_PROC_CPUS 2 +#define SUN55I_PROC_RV 3 + +#define SUN55I_MAX_PROCESSORS 4 +#define SUN55I_CHANS_PER_PROC 4 +#define SUN55I_NUM_ROUTES (SUN55I_MAX_PROCESSORS - 1) +#define SUN55I_NUM_CHANS (SUN55I_NUM_ROUTES * SUN55I_CHANS_PER_PROC) +#define SUN55I_FIFO_MAX 8 + +#define SUNXI_MSGBOX_OFFSET(n) (0x100 * (n)) +#define SUNXI_MSGBOX_READ_IRQ_ENABLE(n) (0x020 + SUNXI_MSGBOX_OFFSET(n)) +#define SUNXI_MSGBOX_READ_IRQ_STATUS(n) (0x024 + SUNXI_MSGBOX_OFFSET(n)) +#define SUNXI_MSGBOX_WRITE_IRQ_ENABLE(n) (0x030 + SUNXI_MSGBOX_OFFSET(n)) +#define SUNXI_MSGBOX_WRITE_IRQ_STATUS(n) (0x034 + SUNXI_MSGBOX_OFFSET(n)) +#define SUNXI_MSGBOX_FIFO_STATUS(n, p) (0x050 + SUNXI_MSGBOX_OFFSET(n) + 0x4 * (p)) +#define SUNXI_MSGBOX_MSG_STATUS(n, p) (0x060 + SUNXI_MSGBOX_OFFSET(n) + 0x4 * (p)) +#define SUNXI_MSGBOX_MSG_FIFO(n, p) (0x070 + SUNXI_MSGBOX_OFFSET(n) + 0x4 * (p)) + +#define RD_IRQ_EN_BIT(p) BIT((p) * 2) +#define RD_IRQ_PEND_BIT(p) BIT((p) * 2) +#define MSG_NUM_MASK GENMASK(3, 0) + +struct sun55i_route { + u8 remote_id; + u8 remote_n; +}; + +struct sun55i_msgbox { + struct mbox_controller controller; + void __iomem *regs[SUN55I_MAX_PROCESSORS]; + struct clk *clk; + struct reset_control *reset; + int irqs[SUN55I_MAX_PROCESSORS]; + int num_irqs; + /* Protects concurrent MMIO register access */ + spinlock_t lock; +}; + +extern const struct sun55i_route sun55i_msgbox_arm_routes[SUN55I_NUM_ROUTES]; +extern const struct mbox_chan_ops sun55i_msgbox_chan_ops; + +void sun55i_chan_to_route(int chan_idx, int *local_n, int *p, + int *remote_id, int *remote_n); +irqreturn_t sun55i_msgbox_irq(int irq, void *dev_id); + +#endif /* _SUN55I_MSGBOX_H_ */ From patchwork Sun Sep 27 00:20:12 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tim Michals X-Patchwork-Id: 3491 Return-Path: X-Original-To: noreply@patchwork.local Delivered-To: noreply@patchwork.local Received: from sea.lore.kernel.org (sea.lore.kernel.org [172.234.253.10]) by mxe881.netcup.net (Postfix) with ESMTPS id 675151C1D46 for ; Sun, 27 Sep 2026 02:21:54 +0200 (CEST) Authentication-Results: mxe881; dkim=pass header.d=gmail.com; spf=pass (sender IP is 172.234.253.10) smtp.mailfrom=linux-sunxi+bounces-26293-noreply=patchwork.local@lists.linux.dev smtp.helo=sea.lore.kernel.org Received-SPF: pass (mxe881: domain of lists.linux.dev designates 172.234.253.10 as permitted sender) client-ip=172.234.253.10; envelope-from=linux-sunxi+bounces-26293-noreply=patchwork.local@lists.linux.dev; helo=sea.lore.kernel.org; Received: from smtp.subspace.kernel.org (conduit.subspace.kernel.org [100.90.174.1]) by sea.lore.kernel.org (Postfix) with ESMTP id 1F3AF33824 for ; Sun, 27 Sep 2026 00:20:59 +0000 (UTC) Received: from localhost.localdomain (localhost.localdomain [127.0.0.1]) by smtp.subspace.kernel.org (Postfix) with ESMTP id EABD62367B5; Sun, 27 Sep 2026 00:20:58 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="W7i4frmQ" X-Original-To: linux-sunxi@lists.linux.dev Received: from mail-oo2-f42.google.com (mail-oo2-f42.google.com [74.125.231.170]) (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 1C12522538F for ; Sun, 27 Sep 2026 00:20:55 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.231.170 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790468458; cv=none; b=q1cHAcakWpI+37s6Wak/i76yowpte9nfAHXbPxVEcJ0jyx2pohXPMSojSJYs69N27kqXTAKJtiBlVsdHNYZf2HVJMGYA2GTiNMbQW9zNsvAuIceJJMh6aaK5YUrQl0VHfccSXqVZ2ExhHw97cuR1ptbbvf/7IfAKXnWtZro4YTk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790468458; c=relaxed/simple; bh=rcKuXtoi/+BWN55BgSx5mR8j//yFr9+a0/wiKFgXxcM=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=aE1tVmbMyhTG6mCNM3VOd15cHOrMLe5jeWnJCYGEfRUqKcyRNQ06anfPenEZZxVuvBZwkxMuD6G6BdLKeMgITz8gjyfQGAuXXS8swQSx6lbI1ek2pF4XzF0hqyZWaQYSYa3aCrfImctH2lqKFtH+cWy3q9ERn1nmx6E3HPTEYEc= 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=W7i4frmQ; arc=none smtp.client-ip=74.125.231.170 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 Received: by mail-oo2-f42.google.com with SMTP id 006d021491bc7-6d8709e0e13so72060eaf.3 for ; Sat, 26 Sep 2026 17:20:55 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1790468454; x=1791073254; darn=lists.linux.dev; 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=DGC6lpugxiBNtmwqdj25s2QmSjjlV1pqtWqexxuTTm0=; b=W7i4frmQ7LxP/lkf0OSXogHG5V1RW8wvsD+eYkQKi5wfz6ZpVEMBfJzCaXEqowmPds 5chqmGShPVonoAOs5fah5HKOERlQFhP5QDhO+ugCOhOqfH5515hGq+B66hr8xEJRv87i hEe3epltlOFGYLOFGJuq2n7kPqAdOCyALvXeOlBJCsPm10Rbmd1Vwx3qdE9VMGH3jiFs cuuGQZcfTSuCF2ClbjrdWzQzwbHDEQ+mq6ewGHxiSdx817VHSftlC5YmfVIHPHu6jsB6 Fp4cNh4p2+/35GT9kMvRDMQuWWB7FiSruF19HYXAQbIQbTAtkHpZbNrVIZ/AObG1h9Fa nipg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1790468454; x=1791073254; 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=DGC6lpugxiBNtmwqdj25s2QmSjjlV1pqtWqexxuTTm0=; b=ZqvqDSUjQ8dMzk5iyCSa+QY1KQTPN7Qz0aFEv4Yg6Qj0iuzsb1aKt7uNNKBYDeXO1R Dh3KNyOevHQHHmC9cZD8Ab7As/ERfPwLs88tPVUlJelDV56+wkTohWQ9x4ddj4Q5lk51 UJXZVb4JdmBUPkOl7/s2xPYgan0PCp0j+GdGIowQ6dMDReFIE0BGD9QHOnli15vENgJE KL6ahblMiHJz6A9xpbY7gsJc9D9cHUS32aB3oj9BY4pLmYMn43L9jE86aj/UygJdHG/X jQQFl8lzJCkH1tM0bRPhODOjtqRShHwFwx8x+krTCdDgAav2fRglKTCs/9jYcmNj1qH7 N9oQ== X-Gm-Message-State: AFuF++lFqU09OTNb6W12NqzmQOSoKL6K6T5Z7+VtgyVtjbvU7B7gYUSE dmLpewBcHBjYnAU/0ohsyOEg9iD8BGHeHBFkIhvMskQ79eRpdD3lPmgZk/ebxaYR X-Gm-Gg: AYBFou1MNGMpVPPGusPbipicmUhacFLr9agTYVS0XjZ3yO1rQ07T1SoQ0lFic99VFYE 3Sa9mINeDjzX8Q0ibiipNkZL2L1S27v0j1xLafrjokDp8WqL4avLAIGyclOE3cSmLRTOQ/LGs1z D1l6ZXPqV8Mbx2MdHGN7olRYmSK8K+skA4cjW809ZT0Wm1fIDBjU87s7kJuDNsmBFBJ9jKfDE84 c7YpJ+MLg/RSI7Wxti7AbRF5oBU35HpIUIutIzXr9RWamt+2na7uXA2Go2geAg99droHirSQCKD QHgfZspNQeCo3loN5p847hwyEkIrc0vkTJpaKhO10J96CCV+EKHim7EmBLXD6vakswtgooBradr 4zXpjMHeUErISLMH0KoKaLmJC4nI8yH5JF+YVCbCM6nk4OGmXyVFLt1wJuDYYctJDdMjA7Xf075 JgttHz0KEogEarAZXPtXbkkDTgStQmKTUlquXj7mzhocANlakjHBRHcgUQgi5Rj4Uze3TnmL+o1 GHgxAY60TI0kg5O41rS6Q== X-Received: by 2002:a05:6820:8119:b0:6be:7782:5a5f with SMTP id 006d021491bc7-6d43f2cb7c8mr10238446eaf.5.1790468453804; Sat, 26 Sep 2026 17:20:53 -0700 (PDT) Received: from tcmichals-5759.lan ([2607:fb91:3584:35a9:66f8:2cb5:aa80:21fd]) by smtp.gmail.com with ESMTPSA id 006d021491bc7-6d8828e1ea0sm838989eaf.6.2026.09.26.17.20.51 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 26 Sep 2026 17:20:53 -0700 (PDT) From: Tim Michals To: linux-sunxi@lists.linux.dev, linux-remoteproc@vger.kernel.org Cc: devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, wens@kernel.org, jernej.skrabec@gmail.com, samuel@sholland.org, andersson@kernel.org, mathieu.poirier@linaro.org, jassisinghbrar@gmail.com, robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org, Tim Michals Subject: [PATCH v2 3/7] mailbox: sun55i: add KUnit test suite for sun55i msgbox driver Date: Sat, 26 Sep 2026 19:20:12 -0500 Message-ID: <20260927002021.797069-4-tcmichals@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260927002021.797069-1-tcmichals@gmail.com> References: <20260922034711.190253-1-tcmichals@gmail.com> <20260927002021.797069-1-tcmichals@gmail.com> Precedence: bulk X-Mailing-List: linux-sunxi@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-MORS-Enabled: yes X-MORS-DOMAIN: patchwork.local X-MORS-HOSTING: hosting172546 X-MORS-USER: hosting172546 X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= Add comprehensive in-kernel KUnit unit test suite (34 tests) for the Allwinner sun55i hardware Message Box driver. Tests validate: - Channel routing calculations and boundary limits across all 12 channels. - Register offset and bit formula accuracy against hardware specifications. - Mock MMIO message sending, FIFO status polling, and backpressure. - Stale FIFO drain on startup/shutdown capped at SUN55I_FIFO_MAX. - Hardirq multi-port concurrency and simultaneous 3-route message bursts. - Hardware FIFO pop emulation and crosstalk isolation across CPUS/DSP/RV. Signed-off-by: Tim Michals --- drivers/mailbox/Kconfig | 10 + drivers/mailbox/Makefile | 1 + drivers/mailbox/sun55i_msgbox_test.c | 823 +++++++++++++++++++++++++++ 3 files changed, 834 insertions(+) create mode 100644 drivers/mailbox/sun55i_msgbox_test.c diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig index 9067f5161276..af51794d260d 100644 --- a/drivers/mailbox/Kconfig +++ b/drivers/mailbox/Kconfig @@ -342,6 +342,16 @@ config SUN55I_MSGBOX provides 12 hardware FIFO channels for communication between the Cortex-A55 host, CPUS, HiFi4 DSP, and XuanTie E907 RISC-V coprocessors. +config SUN55I_MSGBOX_KUNIT_TEST + tristate "KUnit tests for Allwinner sun55i msgbox" if !KUNIT_ALL_TESTS + depends on KUNIT + default KUNIT_ALL_TESTS + help + KUnit tests for the Allwinner sun55i-msgbox driver, covering + channel routing table entries, register offset macros, and IRQ bit + positions for communication with CPUS, HiFi4 DSP, and XuanTie RISC-V. + Say Y here to run these tests during boot or via kunit.py. + config SPRD_MBOX tristate "Spreadtrum Mailbox" depends on ARCH_SPRD || COMPILE_TEST diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile index aa17f9d0acd4..6fe1f1112c10 100644 --- a/drivers/mailbox/Makefile +++ b/drivers/mailbox/Makefile @@ -71,6 +71,7 @@ obj-$(CONFIG_ZYNQMP_IPI_MBOX) += zynqmp-ipi-mailbox.o obj-$(CONFIG_SUN6I_MSGBOX) += sun6i-msgbox.o obj-$(CONFIG_SUN55I_MSGBOX) += sun55i-msgbox.o +obj-$(CONFIG_SUN55I_MSGBOX_KUNIT_TEST) += sun55i_msgbox_test.o obj-$(CONFIG_SPRD_MBOX) += sprd-mailbox.o diff --git a/drivers/mailbox/sun55i_msgbox_test.c b/drivers/mailbox/sun55i_msgbox_test.c new file mode 100644 index 000000000000..19041b4225da --- /dev/null +++ b/drivers/mailbox/sun55i_msgbox_test.c @@ -0,0 +1,823 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * KUnit tests for Allwinner sun55i 4-port Message Box driver (sun55i-msgbox.c) + * + * Comprehensive, industry-standard test suite validating: + * - Full 12-channel routing table covering CPUS, DSP, and XuanTie RISC-V cores + * - Boundary and out-of-range channel limits + * - Register offset formulas and IRQ bitmask macros + * - Functional driver ops (send_data, last_tx_done, peek_data, startup, shutdown) + * - Exhaustive 12-channel physical destination register write verification + * - Full threshold sweeps for last_tx_done (0..15) and peek_data (0..15) + * - Multi-message burst FIFO receiving with exact payload sequencing + * - Anti-lockup hardirq bounded loop guarantee (FIFO_MAX limit) + * - Multi-channel and multi-port concurrency in a single hardirq pass + * - Stale FIFO purging on startup under varying depths (0, 4, stuck) + * - Interrupt isolation between channels sharing the same port + * + * Directly invokes driver operations using mock register banks. + * + * Copyright (C) 2026 Tim Michals + */ + +#include +#include +#include +#include "sun55i-msgbox.h" + +/* =============== Channel Routing Table Tests =============== */ + +static void test_chan_to_route_cpus_all(struct kunit *test) +{ + int local_n, p, remote_id, remote_n; + + /* Ch 0..3: CPUS (remote_id = SUN55I_PROC_CPUS, remote_n = 0, local_n = 0) */ + sun55i_chan_to_route(0, &local_n, &p, &remote_id, &remote_n); + KUNIT_EXPECT_EQ(test, local_n, 0); + KUNIT_EXPECT_EQ(test, p, 0); + KUNIT_EXPECT_EQ(test, remote_id, SUN55I_PROC_CPUS); + KUNIT_EXPECT_EQ(test, remote_n, 0); + + sun55i_chan_to_route(1, &local_n, &p, &remote_id, &remote_n); + KUNIT_EXPECT_EQ(test, local_n, 0); + KUNIT_EXPECT_EQ(test, p, 1); + KUNIT_EXPECT_EQ(test, remote_id, SUN55I_PROC_CPUS); + KUNIT_EXPECT_EQ(test, remote_n, 0); + + sun55i_chan_to_route(2, &local_n, &p, &remote_id, &remote_n); + KUNIT_EXPECT_EQ(test, local_n, 0); + KUNIT_EXPECT_EQ(test, p, 2); + KUNIT_EXPECT_EQ(test, remote_id, SUN55I_PROC_CPUS); + KUNIT_EXPECT_EQ(test, remote_n, 0); + + sun55i_chan_to_route(3, &local_n, &p, &remote_id, &remote_n); + KUNIT_EXPECT_EQ(test, local_n, 0); + KUNIT_EXPECT_EQ(test, p, 3); + KUNIT_EXPECT_EQ(test, remote_id, SUN55I_PROC_CPUS); + KUNIT_EXPECT_EQ(test, remote_n, 0); +} + +static void test_chan_to_route_dsp_all(struct kunit *test) +{ + int local_n, p, remote_id, remote_n; + + /* Ch 4..7: DSP (remote_id = SUN55I_PROC_DSP, remote_n = 0, local_n = 1) */ + sun55i_chan_to_route(4, &local_n, &p, &remote_id, &remote_n); + KUNIT_EXPECT_EQ(test, local_n, 1); + KUNIT_EXPECT_EQ(test, p, 0); + KUNIT_EXPECT_EQ(test, remote_id, SUN55I_PROC_DSP); + KUNIT_EXPECT_EQ(test, remote_n, 0); + + sun55i_chan_to_route(5, &local_n, &p, &remote_id, &remote_n); + KUNIT_EXPECT_EQ(test, local_n, 1); + KUNIT_EXPECT_EQ(test, p, 1); + KUNIT_EXPECT_EQ(test, remote_id, SUN55I_PROC_DSP); + KUNIT_EXPECT_EQ(test, remote_n, 0); + + sun55i_chan_to_route(6, &local_n, &p, &remote_id, &remote_n); + KUNIT_EXPECT_EQ(test, local_n, 1); + KUNIT_EXPECT_EQ(test, p, 2); + KUNIT_EXPECT_EQ(test, remote_id, SUN55I_PROC_DSP); + KUNIT_EXPECT_EQ(test, remote_n, 0); + + sun55i_chan_to_route(7, &local_n, &p, &remote_id, &remote_n); + KUNIT_EXPECT_EQ(test, local_n, 1); + KUNIT_EXPECT_EQ(test, p, 3); + KUNIT_EXPECT_EQ(test, remote_id, SUN55I_PROC_DSP); + KUNIT_EXPECT_EQ(test, remote_n, 0); +} + +static void test_chan_to_route_rv_all(struct kunit *test) +{ + int local_n, p, remote_id, remote_n; + + /* Ch 8..11: XuanTie RISC-V (remote_id = SUN55I_PROC_RV, remote_n = 2, local_n = 2) */ + sun55i_chan_to_route(8, &local_n, &p, &remote_id, &remote_n); + KUNIT_EXPECT_EQ(test, local_n, 2); + KUNIT_EXPECT_EQ(test, p, 0); + KUNIT_EXPECT_EQ(test, remote_id, SUN55I_PROC_RV); + KUNIT_EXPECT_EQ(test, remote_n, 2); + + sun55i_chan_to_route(9, &local_n, &p, &remote_id, &remote_n); + KUNIT_EXPECT_EQ(test, local_n, 2); + KUNIT_EXPECT_EQ(test, p, 1); + KUNIT_EXPECT_EQ(test, remote_id, SUN55I_PROC_RV); + KUNIT_EXPECT_EQ(test, remote_n, 2); + + sun55i_chan_to_route(10, &local_n, &p, &remote_id, &remote_n); + KUNIT_EXPECT_EQ(test, local_n, 2); + KUNIT_EXPECT_EQ(test, p, 2); + KUNIT_EXPECT_EQ(test, remote_id, SUN55I_PROC_RV); + KUNIT_EXPECT_EQ(test, remote_n, 2); + + sun55i_chan_to_route(11, &local_n, &p, &remote_id, &remote_n); + KUNIT_EXPECT_EQ(test, local_n, 2); + KUNIT_EXPECT_EQ(test, p, 3); + KUNIT_EXPECT_EQ(test, remote_id, SUN55I_PROC_RV); + KUNIT_EXPECT_EQ(test, remote_n, 2); +} + +static void test_chan_to_route_boundary_limits(struct kunit *test) +{ + int local_n, p, remote_id, remote_n; + + /* Channel 0 (lowest valid) */ + sun55i_chan_to_route(0, &local_n, &p, &remote_id, &remote_n); + KUNIT_EXPECT_EQ(test, local_n, 0); + KUNIT_EXPECT_EQ(test, p, 0); + + /* Channel 11 (highest valid) */ + sun55i_chan_to_route(11, &local_n, &p, &remote_id, &remote_n); + KUNIT_EXPECT_EQ(test, local_n, 2); + KUNIT_EXPECT_EQ(test, p, 3); +} + +static void test_chan_to_route_invalid_channels(struct kunit *test) +{ + int local_n, p, remote_id, remote_n; + + /* Negative channel index -> safely clamped to 0 */ + sun55i_chan_to_route(-1, &local_n, &p, &remote_id, &remote_n); + KUNIT_EXPECT_EQ(test, local_n, 0); + KUNIT_EXPECT_EQ(test, p, 0); + KUNIT_EXPECT_EQ(test, remote_id, 0); + KUNIT_EXPECT_EQ(test, remote_n, 0); + + /* Out of range channel 12 -> safely clamped to 0 */ + sun55i_chan_to_route(12, &local_n, &p, &remote_id, &remote_n); + KUNIT_EXPECT_EQ(test, local_n, 0); + KUNIT_EXPECT_EQ(test, p, 0); + KUNIT_EXPECT_EQ(test, remote_id, 0); + KUNIT_EXPECT_EQ(test, remote_n, 0); + + /* Far out of range channel 100 -> safely clamped to 0 */ + sun55i_chan_to_route(100, &local_n, &p, &remote_id, &remote_n); + KUNIT_EXPECT_EQ(test, local_n, 0); + KUNIT_EXPECT_EQ(test, p, 0); + KUNIT_EXPECT_EQ(test, remote_id, 0); + KUNIT_EXPECT_EQ(test, remote_n, 0); +} + +/* =============== Register Offset Macro Tests =============== */ + +static void test_msgbox_offset_values(struct kunit *test) +{ + KUNIT_EXPECT_EQ(test, SUNXI_MSGBOX_OFFSET(0), 0x000); + KUNIT_EXPECT_EQ(test, SUNXI_MSGBOX_OFFSET(1), 0x100); + KUNIT_EXPECT_EQ(test, SUNXI_MSGBOX_OFFSET(2), 0x200); + KUNIT_EXPECT_EQ(test, SUNXI_MSGBOX_OFFSET(3), 0x300); +} + +static void test_msgbox_irq_reg_offsets(struct kunit *test) +{ + KUNIT_EXPECT_EQ(test, SUNXI_MSGBOX_READ_IRQ_ENABLE(0), 0x020); + KUNIT_EXPECT_EQ(test, SUNXI_MSGBOX_READ_IRQ_STATUS(0), 0x024); + KUNIT_EXPECT_EQ(test, SUNXI_MSGBOX_WRITE_IRQ_ENABLE(0), 0x030); + KUNIT_EXPECT_EQ(test, SUNXI_MSGBOX_WRITE_IRQ_STATUS(0), 0x034); + + KUNIT_EXPECT_EQ(test, SUNXI_MSGBOX_READ_IRQ_ENABLE(1), 0x120); + KUNIT_EXPECT_EQ(test, SUNXI_MSGBOX_READ_IRQ_STATUS(1), 0x124); + KUNIT_EXPECT_EQ(test, SUNXI_MSGBOX_READ_IRQ_ENABLE(2), 0x220); + KUNIT_EXPECT_EQ(test, SUNXI_MSGBOX_READ_IRQ_STATUS(2), 0x224); +} + +static void test_msgbox_fifo_reg_offsets(struct kunit *test) +{ + KUNIT_EXPECT_EQ(test, SUNXI_MSGBOX_FIFO_STATUS(0, 0), 0x050); + KUNIT_EXPECT_EQ(test, SUNXI_MSGBOX_FIFO_STATUS(0, 3), 0x05c); + KUNIT_EXPECT_EQ(test, SUNXI_MSGBOX_MSG_STATUS(0, 0), 0x060); + KUNIT_EXPECT_EQ(test, SUNXI_MSGBOX_MSG_STATUS(0, 3), 0x06c); + KUNIT_EXPECT_EQ(test, SUNXI_MSGBOX_MSG_FIFO(0, 0), 0x070); + KUNIT_EXPECT_EQ(test, SUNXI_MSGBOX_MSG_FIFO(0, 3), 0x07c); + + /* Port 2 (RV remote in sun55i_msgbox_arm_routes[2]) */ + KUNIT_EXPECT_EQ(test, SUNXI_MSGBOX_MSG_FIFO(2, 0), 0x270); + KUNIT_EXPECT_EQ(test, SUNXI_MSGBOX_MSG_FIFO(2, 3), 0x27c); +} + +static void test_msgbox_irq_bit_positions(struct kunit *test) +{ + KUNIT_EXPECT_EQ(test, RD_IRQ_EN_BIT(0), BIT(0)); + KUNIT_EXPECT_EQ(test, RD_IRQ_EN_BIT(1), BIT(2)); + KUNIT_EXPECT_EQ(test, RD_IRQ_EN_BIT(2), BIT(4)); + KUNIT_EXPECT_EQ(test, RD_IRQ_EN_BIT(3), BIT(6)); + + KUNIT_EXPECT_EQ(test, RD_IRQ_PEND_BIT(0), BIT(0)); + KUNIT_EXPECT_EQ(test, RD_IRQ_PEND_BIT(1), BIT(2)); + KUNIT_EXPECT_EQ(test, RD_IRQ_PEND_BIT(2), BIT(4)); + KUNIT_EXPECT_EQ(test, RD_IRQ_PEND_BIT(3), BIT(6)); +} + +static void test_msgbox_constants(struct kunit *test) +{ + KUNIT_EXPECT_EQ(test, SUN55I_MAX_PROCESSORS, 4); + KUNIT_EXPECT_EQ(test, SUN55I_CHANS_PER_PROC, 4); + KUNIT_EXPECT_EQ(test, SUN55I_NUM_CHANS, 12); + KUNIT_EXPECT_EQ(test, SUN55I_FIFO_MAX, 8); + KUNIT_EXPECT_EQ(test, (u32)MSG_NUM_MASK, 0xfU); +} + +/* =============== Functional Driver Ops Tests (Mock MMIO) =============== */ + +struct mock_rx_sink { + struct mbox_client client; + int count; + u32 last_msg; + u32 msgs[SUN55I_FIFO_MAX * 2]; + u32 *status_reg; +}; + +struct mock_msgbox_fixture { + struct sun55i_msgbox mbox; + struct mbox_chan chans[SUN55I_NUM_CHANS]; + struct mock_rx_sink sinks[SUN55I_NUM_CHANS]; + u32 regs[SUN55I_MAX_PROCESSORS][0x400 / 4]; +}; + +static void mock_rx_cb(struct mbox_client *cl, void *data) +{ + struct mock_rx_sink *sink = container_of(cl, struct mock_rx_sink, client); + + if (sink->count < ARRAY_SIZE(sink->msgs)) + sink->msgs[sink->count] = *(u32 *)data; + sink->count++; + sink->last_msg = *(u32 *)data; + + /* + * Emulate hardware FIFO pop behavior: reading a word from the + * message FIFO decrements MSG_STATUS in real hardware. + */ + if (sink->status_reg && (*sink->status_reg & MSG_NUM_MASK) > 0) + (*sink->status_reg)--; +} + +static struct mock_msgbox_fixture *create_mock_fixture(struct kunit *test) +{ + struct mock_msgbox_fixture *fix; + int i; + + fix = kunit_kzalloc(test, sizeof(*fix), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, fix); + + spin_lock_init(&fix->mbox.lock); + fix->mbox.controller.chans = fix->chans; + fix->mbox.controller.num_chans = SUN55I_NUM_CHANS; + + for (i = 0; i < SUN55I_MAX_PROCESSORS; i++) + fix->mbox.regs[i] = (void __iomem *)fix->regs[i]; + + for (i = 0; i < SUN55I_NUM_CHANS; i++) { + int local_n, p, remote_id, remote_n; + + sun55i_chan_to_route(i, &local_n, &p, &remote_id, &remote_n); + fix->chans[i].con_priv = &fix->mbox; + fix->sinks[i].client.rx_callback = mock_rx_cb; + fix->sinks[i].status_reg = &fix->regs[0][SUNXI_MSGBOX_MSG_STATUS(local_n, p) / 4]; + fix->chans[i].cl = &fix->sinks[i].client; + } + + return fix; +} + +static void test_functional_send_data_all_twelve_channels(struct kunit *test) +{ + struct mock_msgbox_fixture *fix = create_mock_fixture(test); + int ch; + + /* Send unique test pattern over every channel from 0 to 11 */ + for (ch = 0; ch < SUN55I_NUM_CHANS; ch++) { + int local_n, p, remote_id, remote_n; + u32 val = 0x55000000U | (u32)ch; + u32 reg_idx; + + sun55i_chan_to_route(ch, &local_n, &p, &remote_id, &remote_n); + sun55i_msgbox_chan_ops.send_data(&fix->chans[ch], &val); + + reg_idx = SUNXI_MSGBOX_MSG_FIFO(remote_n, p) / 4; + KUNIT_EXPECT_EQ(test, fix->regs[remote_id][reg_idx], val); + } +} + +static void test_functional_send_data_null(struct kunit *test) +{ + struct mock_msgbox_fixture *fix = create_mock_fixture(test); + u32 reg_idx; + + /* NULL data pointer writes 0 to FIFO without dereferencing */ + sun55i_msgbox_chan_ops.send_data(&fix->chans[8], NULL); + + reg_idx = SUNXI_MSGBOX_MSG_FIFO(2, 0) / 4; + KUNIT_EXPECT_EQ(test, fix->regs[3][reg_idx], 0U); +} + +static void test_functional_send_data_patterns(struct kunit *test) +{ + struct mock_msgbox_fixture *fix = create_mock_fixture(test); + static const u32 patterns[] = { + 0x00000000, 0xFFFFFFFF, 0x55555555, 0xAAAAAAAA, 0x12345678, + }; + int i; + u32 reg_idx = SUNXI_MSGBOX_MSG_FIFO(2, 0) / 4; + + for (i = 0; i < ARRAY_SIZE(patterns); i++) { + sun55i_msgbox_chan_ops.send_data(&fix->chans[8], (void *)&patterns[i]); + KUNIT_EXPECT_EQ(test, fix->regs[3][reg_idx], patterns[i]); + } +} + +static void test_functional_last_tx_done_sweep(struct kunit *test) +{ + struct mock_msgbox_fixture *fix = create_mock_fixture(test); + u32 reg_idx = SUNXI_MSGBOX_MSG_STATUS(2, 0) / 4; + u32 count; + + /* Counts 0..7: FIFO has space -> last_tx_done returns true */ + for (count = 0; count < 8; count++) { + fix->regs[3][reg_idx] = count; + KUNIT_EXPECT_TRUE(test, sun55i_msgbox_chan_ops.last_tx_done(&fix->chans[8])); + } + + /* Counts 8..15: FIFO is full or overflow -> last_tx_done returns false */ + for (count = 8; count <= 15; count++) { + fix->regs[3][reg_idx] = count; + KUNIT_EXPECT_FALSE(test, sun55i_msgbox_chan_ops.last_tx_done(&fix->chans[8])); + } +} + +static void test_functional_peek_data_sweep(struct kunit *test) +{ + struct mock_msgbox_fixture *fix = create_mock_fixture(test); + u32 reg_idx = SUNXI_MSGBOX_MSG_STATUS(2, 0) / 4; + u32 count; + + /* Count 0: no messages -> returns false */ + fix->regs[0][reg_idx] = 0; + KUNIT_EXPECT_FALSE(test, sun55i_msgbox_chan_ops.peek_data(&fix->chans[8])); + + /* Counts 1..15: has messages -> returns true */ + for (count = 1; count <= 15; count++) { + fix->regs[0][reg_idx] = count; + KUNIT_EXPECT_TRUE(test, sun55i_msgbox_chan_ops.peek_data(&fix->chans[8])); + } +} + +static void test_functional_startup_and_shutdown(struct kunit *test) +{ + struct mock_msgbox_fixture *fix = create_mock_fixture(test); + u32 en_idx = SUNXI_MSGBOX_READ_IRQ_ENABLE(2) / 4; + u32 stat_idx = SUNXI_MSGBOX_READ_IRQ_STATUS(2) / 4; + + /* Startup on Channel 8: enables IRQ and clears pending status */ + sun55i_msgbox_chan_ops.startup(&fix->chans[8]); + KUNIT_EXPECT_EQ(test, fix->regs[0][en_idx] & RD_IRQ_EN_BIT(0), RD_IRQ_EN_BIT(0)); + KUNIT_EXPECT_EQ(test, fix->regs[0][stat_idx] & RD_IRQ_PEND_BIT(0), RD_IRQ_PEND_BIT(0)); + + /* Shutdown on Channel 8: disables IRQ */ + sun55i_msgbox_chan_ops.shutdown(&fix->chans[8]); + KUNIT_EXPECT_EQ(test, fix->regs[0][en_idx] & RD_IRQ_EN_BIT(0), 0U); +} + +static void test_functional_shutdown_isolation(struct kunit *test) +{ + struct mock_msgbox_fixture *fix = create_mock_fixture(test); + u32 en_idx = SUNXI_MSGBOX_READ_IRQ_ENABLE(0) / 4; + + /* Enable both Channel 0 and Channel 1 on Port 0 */ + sun55i_msgbox_chan_ops.startup(&fix->chans[0]); + sun55i_msgbox_chan_ops.startup(&fix->chans[1]); + KUNIT_EXPECT_EQ(test, fix->regs[0][en_idx] & (RD_IRQ_EN_BIT(0) | RD_IRQ_EN_BIT(1)), + RD_IRQ_EN_BIT(0) | RD_IRQ_EN_BIT(1)); + + /* Shutting down Channel 0 must leave Channel 1 enabled */ + sun55i_msgbox_chan_ops.shutdown(&fix->chans[0]); + KUNIT_EXPECT_EQ(test, fix->regs[0][en_idx] & RD_IRQ_EN_BIT(0), 0U); + KUNIT_EXPECT_EQ(test, fix->regs[0][en_idx] & RD_IRQ_EN_BIT(1), RD_IRQ_EN_BIT(1)); +} + +static void test_functional_startup_flushes_stale_fifo(struct kunit *test) +{ + struct mock_msgbox_fixture *fix = create_mock_fixture(test); + u32 en_idx = SUNXI_MSGBOX_READ_IRQ_ENABLE(2) / 4; + u32 stat_idx = SUNXI_MSGBOX_READ_IRQ_STATUS(2) / 4; + u32 msg_stat_idx = SUNXI_MSGBOX_MSG_STATUS(2, 0) / 4; + u32 fifo_idx = SUNXI_MSGBOX_MSG_FIFO(2, 0) / 4; + int ret; + + /* Simulate stale messages present in hardware FIFO before channel open */ + fix->regs[0][msg_stat_idx] = 4; + fix->regs[0][fifo_idx] = 0xDEADBEEF; + + ret = sun55i_msgbox_chan_ops.startup(&fix->chans[8]); + KUNIT_EXPECT_EQ(test, ret, 0); + + /* IRQ must be enabled and pending status cleared */ + KUNIT_EXPECT_EQ(test, fix->regs[0][en_idx] & RD_IRQ_EN_BIT(0), RD_IRQ_EN_BIT(0)); + KUNIT_EXPECT_EQ(test, fix->regs[0][stat_idx] & RD_IRQ_PEND_BIT(0), RD_IRQ_PEND_BIT(0)); +} + +static void test_functional_startup_capped_at_fifo_max(struct kunit *test) +{ + struct mock_msgbox_fixture *fix = create_mock_fixture(test); + u32 msg_stat_idx = SUNXI_MSGBOX_MSG_STATUS(2, 0) / 4; + u32 fifo_idx = SUNXI_MSGBOX_MSG_FIFO(2, 0) / 4; + int ret; + + /* Hardware defect: MSG_STATUS always returns non-zero */ + fix->regs[0][msg_stat_idx] = 15; + fix->regs[0][fifo_idx] = 0xCAFEBABE; + + /* Must terminate after SUN55I_FIFO_MAX reads and return 0 */ + ret = sun55i_msgbox_chan_ops.startup(&fix->chans[8]); + KUNIT_EXPECT_EQ(test, ret, 0); +} + +static void test_functional_shutdown_flushes_and_bounds(struct kunit *test) +{ + struct mock_msgbox_fixture *fix = create_mock_fixture(test); + u32 en_idx = SUNXI_MSGBOX_READ_IRQ_ENABLE(2) / 4; + u32 stat_idx = SUNXI_MSGBOX_READ_IRQ_STATUS(2) / 4; + u32 msg_stat_idx = SUNXI_MSGBOX_MSG_STATUS(2, 0) / 4; + u32 fifo_idx = SUNXI_MSGBOX_MSG_FIFO(2, 0) / 4; + + /* First startup */ + sun55i_msgbox_chan_ops.startup(&fix->chans[8]); + + /* Simulate residual messages arriving before shutdown */ + fix->regs[0][msg_stat_idx] = 15; + fix->regs[0][fifo_idx] = 0x11223344; + + /* Must terminate after SUN55I_FIFO_MAX without hanging */ + sun55i_msgbox_chan_ops.shutdown(&fix->chans[8]); + KUNIT_EXPECT_EQ(test, fix->regs[0][en_idx] & RD_IRQ_EN_BIT(0), 0U); + KUNIT_EXPECT_EQ(test, fix->regs[0][stat_idx] & RD_IRQ_PEND_BIT(0), RD_IRQ_PEND_BIT(0)); +} + +/* =============== Interrupt Handler (Hardirq) Simulation =============== */ + +static void test_irq_spurious_returns_none(struct kunit *test) +{ + struct mock_msgbox_fixture *fix = create_mock_fixture(test); + irqreturn_t ret; + + /* All IRQ enable and status registers are 0 -> IRQ_NONE */ + ret = sun55i_msgbox_irq(0, &fix->mbox); + KUNIT_EXPECT_EQ(test, ret, IRQ_NONE); +} + +static void test_irq_single_message_received(struct kunit *test) +{ + struct mock_msgbox_fixture *fix = create_mock_fixture(test); + u32 en_idx = SUNXI_MSGBOX_READ_IRQ_ENABLE(2) / 4; + u32 stat_idx = SUNXI_MSGBOX_READ_IRQ_STATUS(2) / 4; + u32 msg_stat_idx = SUNXI_MSGBOX_MSG_STATUS(2, 0) / 4; + u32 fifo_idx = SUNXI_MSGBOX_MSG_FIFO(2, 0) / 4; + irqreturn_t ret; + + /* Configure pending IRQ on Channel 8 (local_n = 2, p = 0) */ + fix->regs[0][en_idx] = RD_IRQ_EN_BIT(0); + fix->regs[0][stat_idx] = RD_IRQ_PEND_BIT(0); + fix->regs[0][msg_stat_idx] = 1; /* 1 message in FIFO */ + fix->regs[0][fifo_idx] = 0xCAFEF00D; + + ret = sun55i_msgbox_irq(0, &fix->mbox); + KUNIT_EXPECT_EQ(test, ret, IRQ_HANDLED); + + /* Verify data delivered to mailbox client */ + KUNIT_EXPECT_EQ(test, fix->sinks[8].count, 1); + KUNIT_EXPECT_EQ(test, fix->sinks[8].last_msg, 0xCAFEF00DU); + + /* Verify write-1-to-clear bit was written */ + KUNIT_EXPECT_EQ(test, fix->regs[0][stat_idx], RD_IRQ_PEND_BIT(0)); +} + +static void test_irq_empty_fifo_status_clear(struct kunit *test) +{ + struct mock_msgbox_fixture *fix = create_mock_fixture(test); + u32 en_idx = SUNXI_MSGBOX_READ_IRQ_ENABLE(2) / 4; + u32 stat_idx = SUNXI_MSGBOX_READ_IRQ_STATUS(2) / 4; + u32 msg_stat_idx = SUNXI_MSGBOX_MSG_STATUS(2, 0) / 4; + irqreturn_t ret; + + /* Pending IRQ bit set, but MSG_STATUS has 0 messages */ + fix->regs[0][en_idx] = RD_IRQ_EN_BIT(0); + fix->regs[0][stat_idx] = RD_IRQ_PEND_BIT(0); + fix->regs[0][msg_stat_idx] = 0; + + ret = sun55i_msgbox_irq(0, &fix->mbox); + KUNIT_EXPECT_EQ(test, ret, IRQ_HANDLED); + + /* No messages should be dispatched to client */ + KUNIT_EXPECT_EQ(test, fix->sinks[8].count, 0); + + /* Status must still be cleared */ + KUNIT_EXPECT_EQ(test, fix->regs[0][stat_idx], RD_IRQ_PEND_BIT(0)); +} + +static void test_irq_multi_channel_concurrency(struct kunit *test) +{ + struct mock_msgbox_fixture *fix = create_mock_fixture(test); + u32 en_idx = SUNXI_MSGBOX_READ_IRQ_ENABLE(0) / 4; + u32 stat_idx = SUNXI_MSGBOX_READ_IRQ_STATUS(0) / 4; + irqreturn_t ret; + + /* Both Channel 0 (p=0) and Channel 1 (p=1) pending on Port 0 */ + fix->regs[0][en_idx] = RD_IRQ_EN_BIT(0) | RD_IRQ_EN_BIT(1); + fix->regs[0][stat_idx] = RD_IRQ_PEND_BIT(0) | RD_IRQ_PEND_BIT(1); + + /* Ch 0 has message 0x11111111 */ + fix->regs[0][SUNXI_MSGBOX_MSG_STATUS(0, 0) / 4] = 1; + fix->regs[0][SUNXI_MSGBOX_MSG_FIFO(0, 0) / 4] = 0x11111111; + + /* Ch 1 has message 0x22222222 */ + fix->regs[0][SUNXI_MSGBOX_MSG_STATUS(0, 1) / 4] = 1; + fix->regs[0][SUNXI_MSGBOX_MSG_FIFO(0, 1) / 4] = 0x22222222; + + ret = sun55i_msgbox_irq(0, &fix->mbox); + KUNIT_EXPECT_EQ(test, ret, IRQ_HANDLED); + + /* Both channels must be serviced in the same interrupt invocation */ + KUNIT_EXPECT_EQ(test, fix->sinks[0].count, 1); + KUNIT_EXPECT_EQ(test, fix->sinks[0].last_msg, 0x11111111U); + + KUNIT_EXPECT_EQ(test, fix->sinks[1].count, 1); + KUNIT_EXPECT_EQ(test, fix->sinks[1].last_msg, 0x22222222U); +} + +static void test_irq_multi_port_concurrency(struct kunit *test) +{ + struct mock_msgbox_fixture *fix = create_mock_fixture(test); + irqreturn_t ret; + + /* Port 0 (Channel 0: CPUS) has pending interrupt */ + fix->regs[0][SUNXI_MSGBOX_READ_IRQ_ENABLE(0) / 4] = RD_IRQ_EN_BIT(0); + fix->regs[0][SUNXI_MSGBOX_READ_IRQ_STATUS(0) / 4] = RD_IRQ_PEND_BIT(0); + fix->regs[0][SUNXI_MSGBOX_MSG_STATUS(0, 0) / 4] = 1; + fix->regs[0][SUNXI_MSGBOX_MSG_FIFO(0, 0) / 4] = 0xAAAAAAAA; + + /* Port 2 (Channel 8: RV) has pending interrupt */ + fix->regs[0][SUNXI_MSGBOX_READ_IRQ_ENABLE(2) / 4] = RD_IRQ_EN_BIT(0); + fix->regs[0][SUNXI_MSGBOX_READ_IRQ_STATUS(2) / 4] = RD_IRQ_PEND_BIT(0); + fix->regs[0][SUNXI_MSGBOX_MSG_STATUS(2, 0) / 4] = 1; + fix->regs[0][SUNXI_MSGBOX_MSG_FIFO(2, 0) / 4] = 0xBBBBBBBB; + + ret = sun55i_msgbox_irq(0, &fix->mbox); + KUNIT_EXPECT_EQ(test, ret, IRQ_HANDLED); + + /* Both ports must be processed in the single pass across local_n = 0..2 */ + KUNIT_EXPECT_EQ(test, fix->sinks[0].count, 1); + KUNIT_EXPECT_EQ(test, fix->sinks[0].last_msg, 0xAAAAAAAAU); + + KUNIT_EXPECT_EQ(test, fix->sinks[8].count, 1); + KUNIT_EXPECT_EQ(test, fix->sinks[8].last_msg, 0xBBBBBBBBU); +} + +static void test_irq_fifo_drain_capped_at_max(struct kunit *test) +{ + struct mock_msgbox_fixture *fix = create_mock_fixture(test); + u32 en_idx = SUNXI_MSGBOX_READ_IRQ_ENABLE(2) / 4; + u32 stat_idx = SUNXI_MSGBOX_READ_IRQ_STATUS(2) / 4; + u32 msg_stat_idx = SUNXI_MSGBOX_MSG_STATUS(2, 0) / 4; + u32 fifo_idx = SUNXI_MSGBOX_MSG_FIFO(2, 0) / 4; + irqreturn_t ret; + + /* + * Simulate hardware defect or runaway remote: MSG_STATUS is permanently + * non-zero. The ISR MUST NOT hang in an infinite loop; it must drain + * at most SUN55I_FIFO_MAX (8) messages and return. + */ + fix->regs[0][en_idx] = RD_IRQ_EN_BIT(0); + fix->regs[0][stat_idx] = RD_IRQ_PEND_BIT(0); + fix->regs[0][msg_stat_idx] = 15; /* Always reports data */ + fix->regs[0][fifo_idx] = 0x12345678; + + ret = sun55i_msgbox_irq(0, &fix->mbox); + KUNIT_EXPECT_EQ(test, ret, IRQ_HANDLED); + + /* Drain must be strictly bounded at SUN55I_FIFO_MAX */ + KUNIT_EXPECT_EQ(test, fix->sinks[8].count, SUN55I_FIFO_MAX); +} + +static void test_irq_channel_crosstalk_isolation(struct kunit *test) +{ + struct mock_msgbox_fixture *fix = create_mock_fixture(test); + u32 en_idx = SUNXI_MSGBOX_READ_IRQ_ENABLE(0) / 4; + u32 stat_idx = SUNXI_MSGBOX_READ_IRQ_STATUS(0) / 4; + irqreturn_t ret; + int ch; + + /* Only Channel 2 (Port 0, p=2) has an interrupt enabled and pending */ + fix->regs[0][en_idx] = RD_IRQ_EN_BIT(2); + fix->regs[0][stat_idx] = RD_IRQ_PEND_BIT(2); + fix->regs[0][SUNXI_MSGBOX_MSG_STATUS(0, 2) / 4] = 1; + fix->regs[0][SUNXI_MSGBOX_MSG_FIFO(0, 2) / 4] = 0x33333333; + + ret = sun55i_msgbox_irq(0, &fix->mbox); + KUNIT_EXPECT_EQ(test, ret, IRQ_HANDLED); + + /* Exactly Channel 2 received the message */ + KUNIT_EXPECT_EQ(test, fix->sinks[2].count, 1); + KUNIT_EXPECT_EQ(test, fix->sinks[2].last_msg, 0x33333333U); + + /* All other 11 channels must have received 0 messages */ + for (ch = 0; ch < SUN55I_NUM_CHANS; ch++) { + if (ch == 2) + continue; + KUNIT_EXPECT_EQ(test, fix->sinks[ch].count, 0); + } +} + +static void test_irq_all_three_routes_simultaneous(struct kunit *test) +{ + struct mock_msgbox_fixture *fix = create_mock_fixture(test); + irqreturn_t ret; + + /* Port 0: Channel 0 (CPUS) */ + fix->regs[0][SUNXI_MSGBOX_READ_IRQ_ENABLE(0) / 4] = RD_IRQ_EN_BIT(0); + fix->regs[0][SUNXI_MSGBOX_READ_IRQ_STATUS(0) / 4] = RD_IRQ_PEND_BIT(0); + fix->regs[0][SUNXI_MSGBOX_MSG_STATUS(0, 0) / 4] = 1; + fix->regs[0][SUNXI_MSGBOX_MSG_FIFO(0, 0) / 4] = 0x11110001; + + /* Port 1: Channel 4 (DSP) */ + fix->regs[0][SUNXI_MSGBOX_READ_IRQ_ENABLE(1) / 4] = RD_IRQ_EN_BIT(0); + fix->regs[0][SUNXI_MSGBOX_READ_IRQ_STATUS(1) / 4] = RD_IRQ_PEND_BIT(0); + fix->regs[0][SUNXI_MSGBOX_MSG_STATUS(1, 0) / 4] = 1; + fix->regs[0][SUNXI_MSGBOX_MSG_FIFO(1, 0) / 4] = 0x22220001; + + /* Port 2: Channel 8 (RV) */ + fix->regs[0][SUNXI_MSGBOX_READ_IRQ_ENABLE(2) / 4] = RD_IRQ_EN_BIT(0); + fix->regs[0][SUNXI_MSGBOX_READ_IRQ_STATUS(2) / 4] = RD_IRQ_PEND_BIT(0); + fix->regs[0][SUNXI_MSGBOX_MSG_STATUS(2, 0) / 4] = 1; + fix->regs[0][SUNXI_MSGBOX_MSG_FIFO(2, 0) / 4] = 0x33330001; + + ret = sun55i_msgbox_irq(0, &fix->mbox); + KUNIT_EXPECT_EQ(test, ret, IRQ_HANDLED); + + /* All three destinations serviced cleanly in one ISR pass */ + KUNIT_EXPECT_EQ(test, fix->sinks[0].count, 1); + KUNIT_EXPECT_EQ(test, fix->sinks[0].last_msg, 0x11110001U); + + KUNIT_EXPECT_EQ(test, fix->sinks[4].count, 1); + KUNIT_EXPECT_EQ(test, fix->sinks[4].last_msg, 0x22220001U); + + KUNIT_EXPECT_EQ(test, fix->sinks[8].count, 1); + KUNIT_EXPECT_EQ(test, fix->sinks[8].last_msg, 0x33330001U); +} + +static void test_irq_disabled_channel_ignored(struct kunit *test) +{ + struct mock_msgbox_fixture *fix = create_mock_fixture(test); + u32 en_idx = SUNXI_MSGBOX_READ_IRQ_ENABLE(0) / 4; + u32 stat_idx = SUNXI_MSGBOX_READ_IRQ_STATUS(0) / 4; + irqreturn_t ret; + + /* Channel 1 has pending bit set, but IRQ_ENABLE is 0 (channel disabled) */ + fix->regs[0][en_idx] = 0; + fix->regs[0][stat_idx] = RD_IRQ_PEND_BIT(1); + fix->regs[0][SUNXI_MSGBOX_MSG_STATUS(0, 1) / 4] = 1; + fix->regs[0][SUNXI_MSGBOX_MSG_FIFO(0, 1) / 4] = 0x12345678; + + ret = sun55i_msgbox_irq(0, &fix->mbox); + /* Must return IRQ_NONE and must NOT deliver message */ + KUNIT_EXPECT_EQ(test, ret, IRQ_NONE); + KUNIT_EXPECT_EQ(test, fix->sinks[1].count, 0); +} + +static void test_irq_spurious_noise_bits(struct kunit *test) +{ + struct mock_msgbox_fixture *fix = create_mock_fixture(test); + irqreturn_t ret; + + /* Non-channel upper bits set, no valid channel enabled */ + fix->regs[0][SUNXI_MSGBOX_READ_IRQ_ENABLE(0) / 4] = 0; + fix->regs[0][SUNXI_MSGBOX_READ_IRQ_STATUS(0) / 4] = 0xFFFF0000U; + + ret = sun55i_msgbox_irq(0, &fix->mbox); + KUNIT_EXPECT_EQ(test, ret, IRQ_NONE); +} + +static void test_functional_last_tx_done_backpressure_boundary(struct kunit *test) +{ + struct mock_msgbox_fixture *fix = create_mock_fixture(test); + u32 reg_idx = SUNXI_MSGBOX_MSG_STATUS(2, 0) / 4; + bool done; + + /* 7 entries in FIFO -> space available (< 8) -> returns true */ + fix->regs[3][reg_idx] = 7; + done = sun55i_msgbox_chan_ops.last_tx_done(&fix->chans[8]); + KUNIT_EXPECT_TRUE(test, done); + + /* 8 entries in FIFO -> full (== SUN55I_FIFO_MAX) -> backpressure active (false) */ + fix->regs[3][reg_idx] = 8; + done = sun55i_msgbox_chan_ops.last_tx_done(&fix->chans[8]); + KUNIT_EXPECT_FALSE(test, done); + + /* 15 entries in FIFO -> full -> backpressure active (false) */ + fix->regs[3][reg_idx] = 15; + done = sun55i_msgbox_chan_ops.last_tx_done(&fix->chans[8]); + KUNIT_EXPECT_FALSE(test, done); +} + +static void test_irq_multi_port_burst_interleaved(struct kunit *test) +{ + struct mock_msgbox_fixture *fix = create_mock_fixture(test); + irqreturn_t ret; + + /* Port 0 (Ch 0: CPUS) has 2 messages */ + fix->regs[0][SUNXI_MSGBOX_READ_IRQ_ENABLE(0) / 4] = RD_IRQ_EN_BIT(0); + fix->regs[0][SUNXI_MSGBOX_READ_IRQ_STATUS(0) / 4] = RD_IRQ_PEND_BIT(0); + fix->regs[0][SUNXI_MSGBOX_MSG_STATUS(0, 0) / 4] = 2; + fix->regs[0][SUNXI_MSGBOX_MSG_FIFO(0, 0) / 4] = 0xAA01; + + /* Port 1 (Ch 4: DSP) has 2 messages */ + fix->regs[0][SUNXI_MSGBOX_READ_IRQ_ENABLE(1) / 4] = RD_IRQ_EN_BIT(0); + fix->regs[0][SUNXI_MSGBOX_READ_IRQ_STATUS(1) / 4] = RD_IRQ_PEND_BIT(0); + fix->regs[0][SUNXI_MSGBOX_MSG_STATUS(1, 0) / 4] = 2; + fix->regs[0][SUNXI_MSGBOX_MSG_FIFO(1, 0) / 4] = 0xBB01; + + /* Port 2 (Ch 8: RV) has 2 messages */ + fix->regs[0][SUNXI_MSGBOX_READ_IRQ_ENABLE(2) / 4] = RD_IRQ_EN_BIT(0); + fix->regs[0][SUNXI_MSGBOX_READ_IRQ_STATUS(2) / 4] = RD_IRQ_PEND_BIT(0); + fix->regs[0][SUNXI_MSGBOX_MSG_STATUS(2, 0) / 4] = 2; + fix->regs[0][SUNXI_MSGBOX_MSG_FIFO(2, 0) / 4] = 0xCC01; + + ret = sun55i_msgbox_irq(0, &fix->mbox); + KUNIT_EXPECT_EQ(test, ret, IRQ_HANDLED); + + /* Check deliveries to respective sinks */ + KUNIT_EXPECT_EQ(test, fix->sinks[0].count, 2); + KUNIT_EXPECT_EQ(test, fix->sinks[4].count, 2); + KUNIT_EXPECT_EQ(test, fix->sinks[8].count, 2); +} + +static void test_msgbox_controller_invariants(struct kunit *test) +{ + struct mock_msgbox_fixture *fix = create_mock_fixture(test); + + KUNIT_EXPECT_EQ(test, fix->mbox.controller.num_chans, SUN55I_NUM_CHANS); + KUNIT_EXPECT_PTR_EQ(test, fix->mbox.controller.chans, &fix->chans[0]); +} + +static void test_msgbox_chan_ops_completeness(struct kunit *test) +{ + KUNIT_EXPECT_NOT_NULL(test, sun55i_msgbox_chan_ops.send_data); + KUNIT_EXPECT_NOT_NULL(test, sun55i_msgbox_chan_ops.startup); + KUNIT_EXPECT_NOT_NULL(test, sun55i_msgbox_chan_ops.shutdown); + KUNIT_EXPECT_NOT_NULL(test, sun55i_msgbox_chan_ops.last_tx_done); + KUNIT_EXPECT_NOT_NULL(test, sun55i_msgbox_chan_ops.peek_data); +} + +/* =============== Test Suite Registration =============== */ + +static struct kunit_case sun55i_msgbox_cases[] = { + /* Routing Table Tests */ + KUNIT_CASE(test_chan_to_route_cpus_all), + KUNIT_CASE(test_chan_to_route_dsp_all), + KUNIT_CASE(test_chan_to_route_rv_all), + KUNIT_CASE(test_chan_to_route_boundary_limits), + KUNIT_CASE(test_chan_to_route_invalid_channels), + /* Register Macro Tests */ + KUNIT_CASE(test_msgbox_offset_values), + KUNIT_CASE(test_msgbox_irq_reg_offsets), + KUNIT_CASE(test_msgbox_fifo_reg_offsets), + KUNIT_CASE(test_msgbox_irq_bit_positions), + KUNIT_CASE(test_msgbox_constants), + /* Functional Driver Ops Tests */ + KUNIT_CASE(test_functional_send_data_all_twelve_channels), + KUNIT_CASE(test_functional_send_data_null), + KUNIT_CASE(test_functional_send_data_patterns), + KUNIT_CASE(test_functional_last_tx_done_sweep), + KUNIT_CASE(test_functional_peek_data_sweep), + KUNIT_CASE(test_functional_startup_and_shutdown), + KUNIT_CASE(test_functional_shutdown_isolation), + KUNIT_CASE(test_functional_startup_flushes_stale_fifo), + KUNIT_CASE(test_functional_startup_capped_at_fifo_max), + KUNIT_CASE(test_functional_shutdown_flushes_and_bounds), + /* Hardirq Simulation Tests */ + KUNIT_CASE(test_irq_spurious_returns_none), + KUNIT_CASE(test_irq_spurious_noise_bits), + KUNIT_CASE(test_irq_disabled_channel_ignored), + KUNIT_CASE(test_irq_single_message_received), + KUNIT_CASE(test_irq_empty_fifo_status_clear), + KUNIT_CASE(test_irq_multi_channel_concurrency), + KUNIT_CASE(test_irq_multi_port_concurrency), + KUNIT_CASE(test_irq_multi_port_burst_interleaved), + KUNIT_CASE(test_irq_fifo_drain_capped_at_max), + KUNIT_CASE(test_irq_channel_crosstalk_isolation), + KUNIT_CASE(test_irq_all_three_routes_simultaneous), + KUNIT_CASE(test_functional_last_tx_done_backpressure_boundary), + /* Ops & Controller Invariants */ + KUNIT_CASE(test_msgbox_chan_ops_completeness), + KUNIT_CASE(test_msgbox_controller_invariants), + {} +}; + +static struct kunit_suite sun55i_msgbox_test_suite = { + .name = "sun55i_msgbox", + .test_cases = sun55i_msgbox_cases, +}; + +kunit_test_suite(sun55i_msgbox_test_suite); + +MODULE_AUTHOR("Tim Michals "); +MODULE_DESCRIPTION("Comprehensive KUnit tests for Allwinner sun55i Message Box driver"); +MODULE_LICENSE("GPL"); From patchwork Sun Sep 27 00:20:13 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tim Michals X-Patchwork-Id: 3492 Return-Path: X-Original-To: noreply@patchwork.local Delivered-To: noreply@patchwork.local Received: from sea.lore.kernel.org (sea.lore.kernel.org [172.234.253.10]) by mxe881.netcup.net (Postfix) with ESMTPS id 94C091C1D46 for ; Sun, 27 Sep 2026 02:21:59 +0200 (CEST) Authentication-Results: mxe881; dkim=pass header.d=gmail.com; spf=pass (sender IP is 172.234.253.10) smtp.mailfrom=linux-sunxi+bounces-26294-noreply=patchwork.local@lists.linux.dev smtp.helo=sea.lore.kernel.org Received-SPF: pass (mxe881: domain of lists.linux.dev designates 172.234.253.10 as permitted sender) client-ip=172.234.253.10; envelope-from=linux-sunxi+bounces-26294-noreply=patchwork.local@lists.linux.dev; helo=sea.lore.kernel.org; Received: from smtp.subspace.kernel.org (conduit.subspace.kernel.org [100.90.174.1]) by sea.lore.kernel.org (Postfix) with ESMTP id E4D07339DB for ; Sun, 27 Sep 2026 00:20:59 +0000 (UTC) Received: from localhost.localdomain (localhost.localdomain [127.0.0.1]) by smtp.subspace.kernel.org (Postfix) with ESMTP id BF86921B192; Sun, 27 Sep 2026 00:20:59 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="g2SFCTQQ" X-Original-To: linux-sunxi@lists.linux.dev Received: from mail-oo2-f38.google.com (mail-oo2-f38.google.com [74.125.231.166]) (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 CE583223DD4 for ; Sun, 27 Sep 2026 00:20:57 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.231.166 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790468459; cv=none; b=PMKBOcf/GjQ/G5WjXESDpikHA0Lc96jGSO9bTcBOcjvOIRiMrDf6GpKhX7I07KJmFB6YPWsY4LF7uSvhJm7DJGzsuGX4lVSC0/3UaL0EHXthbCcZuVZYWeBy6wTo6bLqwytcVNWX+KV1MkmbZiFVvu0Kacuj0YSXFBYGfxkbMfI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790468459; c=relaxed/simple; bh=KWgjeJ/vpWyN9YwcSPtBL1y6nnXc/yhU4T0OpEjImlw=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=f1tjAaYXKlhT9Y1Yi0MEw4e69Gs+tgwd2TBxarKwLjh4ykBTwTd7gCflb2O+en5kzsp1H963SK9FDkVfBnEIA+89sD90Q+RNDUGqo6hQ53LvDhWjhVWDDR2lgBSii524kG2FhXTO7+d1pDtvTIvQaA/5J18aDtrFP6NH+V31/H8= 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=g2SFCTQQ; arc=none smtp.client-ip=74.125.231.166 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 Received: by mail-oo2-f38.google.com with SMTP id 46e09a7af769-81be5042c56so50816a34.0 for ; Sat, 26 Sep 2026 17:20:57 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1790468457; x=1791073257; darn=lists.linux.dev; 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=F7YAaa+JluQRvW+ay+L84vEKLXHaoKpKPtPkeIjCvyE=; b=g2SFCTQQbEFHYYVsvRQSnxW5MUnTO+zB1zuEPQ9QZWIOmT9bxKQ6LO4jXQ3IoT1r6p AoIyZIe1MLlMRrEpbkkMhCm0s4fdKjINq2oHxqZGJ0mVyVTC0KYO3zO68odho+UQxmrM QmoJxPDC3BetvjB1fskMmk6y9aO1INppb40QgO0KgLatEWgqDgrQEBS41PsLnuR42Rbc gTvSC2sJo9P+0xxRxG9bGmXak51YbKbmTdzZuI3ViXA/jp5xhGqbHBdJhtkCLqvQnewG WS1XuH5I4MfvNjo0NZ63ZreLotkKThfmwfti9L5gujKfUhuTxBduMwmrtF3R5JvLmr+P LuFw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1790468457; x=1791073257; 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=F7YAaa+JluQRvW+ay+L84vEKLXHaoKpKPtPkeIjCvyE=; b=mS7+qGlbiMj9SLlKkKTfi3Rt1uf1zfB/vrhL3XznezAKZWKpJwA/JSt6utLDCe43lC hB4cyt1gg0o1FmHdWNM8+4B2LWd0kmScd/hzAPrTfig+GW9SKDv+rlLH3Yt6wSU889aa NWUIj9scU2tu8c7DNKunffNxexuo8gIbm6T/p0KFcBr/nst8FWFANaJP1oNoOM+9cfFt erVOafB7mHPllskQhwQ9oWYeWE874X2Fz7v3X0v0Wn3/Wnbse0JX3DeUaCbgSlO5rINQ 45UFef4mFYhKHeDU/zsrQ/52+PklM6htGUpGt/iQXxds+CBMFZF1SF+jXyqaViiHJIqh gtjg== X-Gm-Message-State: AFuF++lgE4XedzooEcWOaWOuwpzHPteEZgAah+XQksOcGogVvdJCIVpE EaXDCGCa3QWEpxRm9elqjRdJtv30XmKQG+w5KGt7/pbEIWkx7jluaezS4bgwCYzP X-Gm-Gg: AYBFou2xgg6jdgf9foZ0/C6H+vsjqIxkwxc0yc0Qe6X/a73EZBmaRbybT1hZIAAX1+1 6uKlal33JnXoyQWxPr7oqVYdgDtX9nmUN8PKc/EV4quptiNTIKCt7yHNkQ25NtKrkxKJ4Cw89yU v+nzr506gcudUS0ZelVq4kQad7OoBcniXPNH2TO+4vMx+Lj5dvTwc3yDZW152hZCsxuJlUGx266 5V/oSvGYL6JjwBUW/usNC5wXsYP2Iuyjj1GxeqLi8efAFIEbk7fVhsyrYWPL47N2FeJeZICrRWx Hs3ZFr2TZkN4u43/863Lq3MSpIQDGTXRuCBNwVL94YXONSf4A4VRdytoO1NXMLHtP+dNZvkPgEs GKqEPmX/VzECIInG6IR7sDuXLr65/Pc7PlI9Rf1ubVtxlrwsRzGMQKMWBlvi99ZfVm/BPzjuxRD /m/wWYoJiAG0aU3SXhbLEiE55hURP1peLnMBn9t8cyY7Mh0sZbhJEsCbu6ZuG/HpZSCRZC+v4hS iE1a/8f94JCsHz/+CqPSQ== X-Received: by 2002:a05:6820:1c8e:b0:6cd:3ffc:64cb with SMTP id 006d021491bc7-6d5baa47035mr5699972eaf.70.1790468456774; Sat, 26 Sep 2026 17:20:56 -0700 (PDT) Received: from tcmichals-5759.lan ([2607:fb91:3584:35a9:66f8:2cb5:aa80:21fd]) by smtp.gmail.com with ESMTPSA id 006d021491bc7-6d8828e1ea0sm838989eaf.6.2026.09.26.17.20.54 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 26 Sep 2026 17:20:56 -0700 (PDT) From: Tim Michals To: linux-sunxi@lists.linux.dev, linux-remoteproc@vger.kernel.org Cc: devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, wens@kernel.org, jernej.skrabec@gmail.com, samuel@sholland.org, andersson@kernel.org, mathieu.poirier@linaro.org, jassisinghbrar@gmail.com, robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org, Tim Michals Subject: [PATCH v2 4/7] dt-bindings: remoteproc: add Allwinner sun55i-rproc schema Date: Sat, 26 Sep 2026 19:20:13 -0500 Message-ID: <20260927002021.797069-5-tcmichals@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260927002021.797069-1-tcmichals@gmail.com> References: <20260922034711.190253-1-tcmichals@gmail.com> <20260927002021.797069-1-tcmichals@gmail.com> Precedence: bulk X-Mailing-List: linux-sunxi@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Rspamd-Server: rspamd-worker-8404 X-Spamd-Result: default: False [0.34 / 15.00]; BAYES_HAM(-5.50)[100.00%]; RBL_SENDERSCORE(2.00)[172.234.253.10:from]; SUSPICIOUS_RECIPS(1.50)[]; MID_CONTAINS_FROM(1.00)[]; DMARC_POLICY_SOFTFAIL(1.00)[gmail.com : SPF not aligned (relaxed), No valid DKIM,none]; R_MISSING_CHARSET(0.50)[]; MAILLIST(-0.15)[generic]; BAD_REP_POLICIES(0.10)[]; MIME_GOOD(-0.10)[text/plain]; HAS_LIST_UNSUB(-0.01)[]; RCPT_COUNT_TWELVE(0.00)[15]; FREEMAIL_FROM(0.00)[gmail.com]; TO_DN_SOME(0.00)[]; DBL_BLOCKED_OPENRESOLVER(0.00)[sholland.org:email,devicetree.org:url,0.108.203.144:email,sea.lore.kernel.org:rdns,sea.lore.kernel.org:helo]; FORGED_SENDER_MAILLIST(0.00)[]; FREEMAIL_CC(0.00)[vger.kernel.org,lists.infradead.org,kernel.org,gmail.com,sholland.org,linaro.org]; TAGGED_RCPT(0.00)[dt]; PRECEDENCE_BULK(0.00)[]; FROM_HAS_DN(0.00)[]; RCVD_TLS_LAST(0.00)[]; MIME_TRACE(0.00)[0:+]; FORGED_RECIPIENTS_MAILLIST(0.00)[]; R_SPF_ALLOW(0.00)[+ip4:172.234.253.10]; ASN(0.00)[asn:63949, ipnet:172.234.224.0/19, country:SG]; ARC_ALLOW(0.00)[subspace.kernel.org:s=arc-20240116:i=1]; TAGGED_FROM(0.00)[bounces-26294-noreply=patchwork.local]; RCVD_COUNT_FIVE(0.00)[6]; FROM_NEQ_ENVFROM(0.00)[tcmichals@gmail.com,linux-sunxi@lists.linux.dev]; RCVD_VIA_SMTP_AUTH(0.00)[] X-Rspamd-Queue-Id: 94C091C1D46 X-MORS-Enabled: yes X-MORS-DOMAIN: patchwork.local X-MORS-HOSTING: hosting172546 X-MORS-USER: hosting172546 X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= Add Device Tree binding schema for the Allwinner XuanTie E907 RISC-V remote processor subsystem integrated into Allwinner A523, A527, and T527 (sun55i) SoCs. The binding defines the memory resources (configuration registers, SRAM Space 0, SRAM Space 1, and optional remap controller), clocks, resets, reserved memory regions, and hardware mailbox channels. Signed-off-by: Tim Michals --- .../remoteproc/allwinner,sun55i-rproc.yaml | 146 ++++++++++++++++++ 1 file changed, 146 insertions(+) create mode 100644 Documentation/devicetree/bindings/remoteproc/allwinner,sun55i-rproc.yaml diff --git a/Documentation/devicetree/bindings/remoteproc/allwinner,sun55i-rproc.yaml b/Documentation/devicetree/bindings/remoteproc/allwinner,sun55i-rproc.yaml new file mode 100644 index 000000000000..53e3f4faaa76 --- /dev/null +++ b/Documentation/devicetree/bindings/remoteproc/allwinner,sun55i-rproc.yaml @@ -0,0 +1,146 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/remoteproc/allwinner,sun55i-rproc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Allwinner XuanTie E906/E907 RISC-V Remoteproc + +maintainers: + - Jernej Skrabec + - Samuel Holland + - Tim Michals + +description: + The Allwinner T527, A527, and A523 (sun55i) SoCs integrate a T-Head + (XuanTie) E906 or E907 RISC-V co-processor alongside the ARM Cortex-A55 + cluster. The co-processor runs bare-metal firmware loaded and lifecycle- + managed by the Linux remoteproc framework. + + The driver controls CCU-integrated clocks (bus, core) and resets + (cfg, core), programs the hardware boot-vector register, maps + internal SRAM (SRAM_A3) windows, and connects to the Allwinner + hardware mailbox (CPUX_MSGBOX) for VirtIO RPMsg IPC. + +properties: + compatible: + const: allwinner,sun55i-a523-rproc + + reg: + minItems: 1 + maxItems: 4 + description: + Memory-mapped register regions. The following named regions are + supported (all optional except at least one of r_sram or r_sram1) - + "cfg" for RISC-V core control and boot-vector registers, + "r_sram" for dedicated MCU SRAM Space 0, + "r_sram1" for switchable MCU SRAM Space 1, + "remap" for the hardware remap control register. + + reg-names: + minItems: 1 + maxItems: 4 + items: + enum: + - cfg + - r_sram + - r_sram1 + - remap + + clocks: + minItems: 1 + maxItems: 5 + + clock-names: + minItems: 1 + items: + - const: bus + - const: core + - const: sram + - const: msgbox + - const: parent + + resets: + minItems: 1 + maxItems: 4 + + reset-names: + minItems: 1 + items: + - const: cfg + - const: core + - const: sram + - const: msgbox + + mboxes: + maxItems: 2 + description: + Exactly two mailbox channels from the Allwinner CPUX_MSGBOX controller, + one receive channel (RISC-V-to-ARM) and one transmit channel + (ARM-to-RISC-V), used for VirtIO RPMsg kick notifications. + + mbox-names: + items: + - const: rx + - const: tx + + firmware-name: + maxItems: 1 + description: + Name of the ELF firmware image to load from /lib/firmware/. + Defaults to "riscv-firmware.elf" if not specified. + + memory-region: + minItems: 1 + items: + - description: VirtIO vring buffer region (DDR carveout) + - description: DRAM firmware execution carveout + - description: RemoteProc trace buffer carveout + + memory-region-names: + minItems: 1 + items: + - const: vram + - const: dram + - const: trace + + interrupts: + maxItems: 1 + description: + Optional hardware crash-notification interrupt. When present the + driver calls rproc_report_crash() on assertion. + + interrupt-names: + items: + - const: crash + +required: + - compatible + - reg + - reg-names + - clocks + - clock-names + - resets + - reset-names + - mboxes + - mbox-names + +additionalProperties: false + +examples: + - | + remoteproc@7130000 { + compatible = "allwinner,sun55i-a523-rproc"; + reg = <0x07130000 0x1000>, + <0x07280000 0x40000>, + <0x072c0000 0x40000>, + <0x07010364 0x4>; + reg-names = "cfg", "r_sram", "r_sram1", "remap"; + clocks = <&mcu_ccu 3>, <&mcu_ccu 4>; + clock-names = "bus", "core"; + resets = <&mcu_ccu 0>, <&mcu_ccu 1>; + reset-names = "cfg", "core"; + mboxes = <&msgbox 8>, <&msgbox 9>; + mbox-names = "rx", "tx"; + firmware-name = "riscv-firmware.elf"; + }; From patchwork Sun Sep 27 00:20:14 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tim Michals X-Patchwork-Id: 3493 Return-Path: X-Original-To: noreply@patchwork.local Delivered-To: noreply@patchwork.local Received: from sea.lore.kernel.org (sea.lore.kernel.org [172.234.253.10]) by mxe881.netcup.net (Postfix) with ESMTPS id 7292E1C1033 for ; Sun, 27 Sep 2026 02:22:40 +0200 (CEST) Authentication-Results: mxe881; dkim=pass header.d=gmail.com; spf=pass (sender IP is 172.234.253.10) smtp.mailfrom=linux-sunxi+bounces-26295-noreply=patchwork.local@lists.linux.dev smtp.helo=sea.lore.kernel.org Received-SPF: pass (mxe881: domain of lists.linux.dev designates 172.234.253.10 as permitted sender) client-ip=172.234.253.10; envelope-from=linux-sunxi+bounces-26295-noreply=patchwork.local@lists.linux.dev; helo=sea.lore.kernel.org; Received: from smtp.subspace.kernel.org (conduit.subspace.kernel.org [100.90.174.1]) by sea.lore.kernel.org (Postfix) with ESMTP id 8F77834841 for ; Sun, 27 Sep 2026 00:21:04 +0000 (UTC) Received: from localhost.localdomain (localhost.localdomain [127.0.0.1]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 66D5B236453; Sun, 27 Sep 2026 00:21:04 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="U493GN+t" X-Original-To: linux-sunxi@lists.linux.dev Received: from mail-oo2-f43.google.com (mail-oo2-f43.google.com [74.125.231.171]) (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 AE07225333F for ; Sun, 27 Sep 2026 00:21:00 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.231.171 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790468464; cv=none; b=OGLIN5ZSgnUyVvK+gqtyEmamQRWD9YId7uGw43laWp+JVw3Mxd15gtE9SLGaZzbnuk5rhkMgbEpCc4c1TJnjajceOOVhjOaIXF33OXm6T5rDd1oAO+IQzmF0VSEcP+cnpKEbgw12lZwbR+wBNOe0YumOR0aslq2LUtHGgYYyOM4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790468464; c=relaxed/simple; bh=ZtBlmFmSnj28Jk63OVYJAXFxqrnHUz0ntmm01ON0lnA=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=eaUMyPWb24s/0OLPWKRBkWJ4T2ccWh9/Km23Rh8C4z1GUenft8aM7GX+J9YmU0Ru85+3Zt+89i30Godp1KXP1kH5ONtr+zMbyZuIdHOEzinpp47MEXrf/HXM+5V2marXzwKKlUZuZTkaJdNGLHBzCqzqCM3wZs8oUDCbjiqMn0E= 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=U493GN+t; arc=none smtp.client-ip=74.125.231.171 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 Received: by mail-oo2-f43.google.com with SMTP id 46e09a7af769-81b15bca7e0so374634a34.2 for ; Sat, 26 Sep 2026 17:21:00 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1790468460; x=1791073260; darn=lists.linux.dev; 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=vBi9g+SfhQ7cqd288lnow3V3UC8T2QuCG79k15FMCKU=; b=U493GN+tSJrke2b9ST5SXI3leJdvcWPJ0L8PGTXkAZgRBJtPUavF/z47QsM0O09jUE 8pNpu6QF4ooI1TrbsIm1XZP5KhqYspk0idcnRY9l6VBtps3ltdHzYXMzdolqV7nenGxX A8v9OaOk8V1kxLL9JRO6W+hw3QCZcku5YqIcBpiYeCoD/QFEWjLh8oqcI4GPTv2pKEi4 VVY8Viunq857HdAxWC3zGxnhj3QfV0n43xcmdjXO9xqEYm5l9iaFe+erebuCm7InrJT+ lj+Z6aHgGBaqvNUzRiuJ8q6ZuXd+7YREVkCbWb6m6z7mFQLbnwZHKzqZyb2/4yQtjUqP lL7A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1790468460; x=1791073260; 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=vBi9g+SfhQ7cqd288lnow3V3UC8T2QuCG79k15FMCKU=; b=OwwUjRi9r5RkSxJcSGWO/uUcLHQqZwRj7TAu1qAMhc5WS5xau+7mkyLrMeZoySaX1p BBLR46ACkGojV95kTuVi3et5stUwjphKaruKJFg1F6k9A1USbtIXcPMm6gr1H6G3SupF sCuDRrsPevrcXCD3PxbNPHeUaUygOKN66MI3hvbJaPA59zXV2a22LsK2e+hmoPJWL1yR ra5oCDrGuJFE638W2Uzw9Lw72ht0WeO51z+kU/o3exE9vxoT4F84c5xL/seVeU7ywLwy QAD3rbPh7Ch6AAQz3E3VfK0mFXFfC8R6sbpkPxrud/7yEokkKZAaYu0crkK58RNUHgfk mJKA== X-Gm-Message-State: AFuF++nX+CgrJjSGDPzmyVaRwmTtCxq5hz6orhh7CKUQn85riSAL5FwQ jWs2QrSeKzkCm6LAXi3cPMbTOZWFqc69LtScijEOJdKx24P+QEG2Hq0V9NTlp3Yj X-Gm-Gg: AYBFou05E2MFW1ki/W3EUg13zn8boux3EarTEflSu9nklR/E7GEcKLd2t9odlUJ3Lfl P/mDXWGoAYCb7xIrRFQ4p7KRMKY1Mz/j470JkNK/fsOk5pUAoulwv2/ttD5VSXqONXojClbgkTH r7B8/blfXDxCO/QFdZpnbY5sSZfSb2ff4rPmuGMjC9QGIehIkrqds71Kb7nqme5pGUC/q5VkVUB UPh33A1qLbeuT27Q7iXhRtWKguDbmqQYJdhORzgCaKzZW9Cf5Tgfwiolq3TGJeU8LzxZBXl5Nz8 2lAjudi8JSX+x/42gMssXV2NoeeHH7FFS06H9fvj/DNTK3BYSFzjvFSXMSG9/8JHc8jHw/sToKH FIycCX0MJarVJJP5bhuIYKsy6+b7YIdoafEXlsE76Z1hFMwfcVcOCEPUQxbbmVjlOtLvxrE55Vz Ni4rXAUo97LzxDiwgkSCT3UYLO+9kYXWRkP0zzSl9KgF5NLaf+3OIcuuX47NiWQa/LtXtYHc7/W mM7VyqSiSNzrlDjCwAR7A== X-Received: by 2002:a05:6820:151d:b0:6cd:3ffc:ad02 with SMTP id 006d021491bc7-6d441c7e1b1mr9673126eaf.82.1790468459223; Sat, 26 Sep 2026 17:20:59 -0700 (PDT) Received: from tcmichals-5759.lan ([2607:fb91:3584:35a9:66f8:2cb5:aa80:21fd]) by smtp.gmail.com with ESMTPSA id 006d021491bc7-6d8828e1ea0sm838989eaf.6.2026.09.26.17.20.57 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 26 Sep 2026 17:20:58 -0700 (PDT) From: Tim Michals To: linux-sunxi@lists.linux.dev, linux-remoteproc@vger.kernel.org Cc: devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, wens@kernel.org, jernej.skrabec@gmail.com, samuel@sholland.org, andersson@kernel.org, mathieu.poirier@linaro.org, jassisinghbrar@gmail.com, robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org, Tim Michals Subject: [PATCH v2 5/7] remoteproc: sunxi: add Allwinner XuanTie RISC-V remoteproc driver Date: Sat, 26 Sep 2026 19:20:14 -0500 Message-ID: <20260927002021.797069-6-tcmichals@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260927002021.797069-1-tcmichals@gmail.com> References: <20260922034711.190253-1-tcmichals@gmail.com> <20260927002021.797069-1-tcmichals@gmail.com> Precedence: bulk X-Mailing-List: linux-sunxi@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-MORS-Enabled: yes X-MORS-DOMAIN: patchwork.local X-MORS-HOSTING: hosting172546 X-MORS-USER: hosting172546 X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= Add remoteproc driver for the auxiliary Alibaba T-Head XuanTie E907 RISC-V co-processor found on Allwinner A523, A527, and T527 SoCs. Features implemented: - SRAM Space 0 and Space 1 memory window translation via Address Translation Table (ATT) architecture matching imx_rproc conventions. - Dynamic DRAM carveout and CMA buffer allocation for VirtIO RPMsg. - CCU reset and clock lifecycle management with boot address setup prior to core reset de-assertion. - Dual-channel hardware Message Box integration for kick/vq signalling. - Workqueue-deferred RPMsg notification for Linux PREEMPT_RT safety. - Ring-buffer trace0 diagnostic endpoint via debugfs. Signed-off-by: Tim Michals --- drivers/remoteproc/Kconfig | 8 + drivers/remoteproc/Makefile | 1 + drivers/remoteproc/sunxi_rproc.c | 922 +++++++++++++++++++++++++++++++ drivers/remoteproc/sunxi_rproc.h | 116 ++++ 4 files changed, 1047 insertions(+) create mode 100644 drivers/remoteproc/sunxi_rproc.c create mode 100644 drivers/remoteproc/sunxi_rproc.h diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig index 73500fdc227d..d35b033b9106 100644 --- a/drivers/remoteproc/Kconfig +++ b/drivers/remoteproc/Kconfig @@ -388,6 +388,14 @@ config XLNX_R5_REMOTEPROC It's safe to say N if not interested in using RPU r5f cores. +config SUNXI_REMOTEPROC + tristate "Allwinner XuanTie RISC-V remoteproc support" + depends on (ARCH_SUNXI || COMPILE_TEST) && MAILBOX + help + Say y or m here to support the Allwinner XuanTie E906/E907 RISC-V + co-processor on Allwinner A523, A527, and T527 SoCs via the + remote processor framework. + endif # REMOTEPROC endmenu diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile index 689686de0d41..02acefa6d880 100644 --- a/drivers/remoteproc/Makefile +++ b/drivers/remoteproc/Makefile @@ -41,3 +41,4 @@ obj-$(CONFIG_TI_K3_DSP_REMOTEPROC) += ti_k3_dsp_remoteproc.o ti_k3_common.o obj-$(CONFIG_TI_K3_M4_REMOTEPROC) += ti_k3_m4_remoteproc.o ti_k3_common.o obj-$(CONFIG_TI_K3_R5_REMOTEPROC) += ti_k3_r5_remoteproc.o ti_k3_common.o obj-$(CONFIG_XLNX_R5_REMOTEPROC) += xlnx_r5_remoteproc.o +obj-$(CONFIG_SUNXI_REMOTEPROC) += sunxi_rproc.o diff --git a/drivers/remoteproc/sunxi_rproc.c b/drivers/remoteproc/sunxi_rproc.c new file mode 100644 index 000000000000..fbb0ee8a9110 --- /dev/null +++ b/drivers/remoteproc/sunxi_rproc.c @@ -0,0 +1,922 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Allwinner XuanTie E906/E907 RISC-V Remote Processor Driver + * + * Copyright (C) 2024-2026 Allwinner Technology Co., Ltd. + * Copyright (C) 2026 Tim Michals + * + * CCU-integrated remoteproc driver for XuanTie RISC-V co-processors on + * Allwinner SoCs (T527/A527/A523: E906/E907) supporting TCM, SRAM, and DRAM. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "remoteproc_internal.h" +#include "sunxi_rproc.h" + +#define DRIVER_NAME "sunxi-rproc" + +static const struct sunxi_rproc_att sun55i_rproc_att[] = { + /* dev addr (remote) , sys addr (host PA) , size , flags */ + /* Space 0 Core Aliases -> Space 0 Host PA */ + { E907_SRAM_SPACE0_DA, SUN55I_SRAM_SPACE0_SYS, SUN55I_SRAM_SPACE0_SIZE, ATT_IOMEM }, + { E907_SRAM_SPACE0_DA_ALT, SUN55I_SRAM_SPACE0_SYS, SUN55I_SRAM_SPACE0_SIZE, ATT_IOMEM }, + { E907_SRAM_C_DA, SUN55I_SRAM_SPACE0_SYS, SUN55I_SRAM_SPACE0_SIZE, ATT_IOMEM }, + { SUN55I_SRAM_SPACE0_SYS, SUN55I_SRAM_SPACE0_SYS, SUN55I_SRAM_SPACE0_SIZE, ATT_IOMEM }, + + /* Space 1 Core Aliases -> Space 1 Host PA */ + { E907_SRAM_SPACE1_DA, SUN55I_SRAM_SPACE1_SYS, SUN55I_SRAM_SPACE1_SIZE, ATT_IOMEM }, + { E907_SRAM_SPACE1_DA_ALT, SUN55I_SRAM_SPACE1_SYS, SUN55I_SRAM_SPACE1_SIZE, ATT_IOMEM }, + { SUN55I_SRAM_SPACE1_SYS, SUN55I_SRAM_SPACE1_SYS, SUN55I_SRAM_SPACE1_SIZE, ATT_IOMEM }, +}; + +const struct sunxi_rproc_cfg sun55i_riscv_cfg = { + .name = "XuanTie E907 RISC-V", + .att = sun55i_rproc_att, + .att_size = ARRAY_SIZE(sun55i_rproc_att), + .has_remap_reg = true, + .boot_reg_offset = E906_STA_ADD_REG, +}; + +#if IS_ENABLED(CONFIG_SUNXI_REMOTEPROC_KUNIT_TEST) +EXPORT_SYMBOL_GPL(sun55i_riscv_cfg); +#endif + +static void sunxi_rproc_vq_work(struct work_struct *work) +{ + struct sunxi_rproc *priv = container_of(work, struct sunxi_rproc, vq_work); + + rproc_vq_interrupt(priv->rproc, 0); + rproc_vq_interrupt(priv->rproc, 1); +} + +static void sunxi_rproc_mb_rx_callback(struct mbox_client *cl, void *data) +{ + struct sunxi_rproc *priv = container_of(cl, struct sunxi_rproc, cl); + + schedule_work(&priv->vq_work); +} + +static irqreturn_t sunxi_rproc_crash_handler(int irq, void *data) +{ + struct sunxi_rproc *priv = data; + struct rproc *rproc = priv->rproc; + + dev_err(priv->dev, "Hardware crash event received from %s core!\n", + priv->cfg ? priv->cfg->name : "remote"); + if (priv->crash_irq_enabled) { + disable_irq_nosync(irq); + priv->crash_irq_enabled = false; + } + rproc_report_crash(rproc, RPROC_FATAL_ERROR); + + return IRQ_HANDLED; +} + +int sunxi_rproc_prepare(struct rproc *rproc) +{ + struct sunxi_rproc *priv = rproc->priv; + const struct sunxi_rproc_cfg *cfg = priv->cfg ? priv->cfg : &sun55i_riscv_cfg; + int ret; + + /* 1. Deassert configuration & SRAM bus resets */ + if (priv->rst_cfg) { + ret = reset_control_deassert(priv->rst_cfg); + if (ret) { + dev_err(priv->dev, "failed to deassert cfg reset: %d\n", ret); + return ret; + } + } + + if (priv->rst_sram) { + ret = reset_control_deassert(priv->rst_sram); + if (ret) { + dev_err(priv->dev, "failed to deassert sram reset: %d\n", ret); + goto err_assert_cfg; + } + } + + if (priv->rst_msgbox) { + ret = reset_control_deassert(priv->rst_msgbox); + if (ret) { + dev_err(priv->dev, "failed to deassert msgbox reset: %d\n", ret); + goto err_assert_sram; + } + } + + /* 2. Enable parent clock (PLL source) */ + if (priv->clk_parent) { + ret = clk_prepare_enable(priv->clk_parent); + if (ret) { + dev_err(priv->dev, "failed to enable parent clock: %d\n", ret); + goto err_assert_msgbox; + } + } + + /* 3. Enable interconnect bus and SRAM clocks */ + if (priv->clk_bus) { + ret = clk_prepare_enable(priv->clk_bus); + if (ret) { + dev_err(priv->dev, "failed to enable bus clock: %d\n", ret); + goto err_disable_parent; + } + } + + if (priv->clk_sram) { + ret = clk_prepare_enable(priv->clk_sram); + if (ret) { + dev_err(priv->dev, "failed to enable sram clock: %d\n", ret); + goto err_disable_bus; + } + } + + if (priv->clk_msgbox) { + ret = clk_prepare_enable(priv->clk_msgbox); + if (ret) { + dev_err(priv->dev, "failed to enable msgbox clock: %d\n", ret); + goto err_disable_sram_clk; + } + } + + /* 4. Enable core clock */ + if (priv->clk_core) { + ret = clk_prepare_enable(priv->clk_core); + if (ret) { + dev_err(priv->dev, "failed to enable core clock: %d\n", ret); + goto err_disable_msgbox_clk; + } + } + + /* + * 4b. Enable SRAMA3_2 for MCU_SYS (RISC-V) via REMAP_CTRL_REG bit 1. + */ + if (cfg->has_remap_reg && priv->remap_va) { + u32 remap_val = readl(priv->remap_va); + + remap_val |= SUNXI_REMAP_SRAMA3_2_BIT; + writel(remap_val, priv->remap_va); + dev_dbg(priv->dev, "REMAP_CTRL_REG set to 0x%08x (SRAMA3_2 enabled)\n", + readl(priv->remap_va)); + } + + /* + * 5. Cleanly clear Dedicated Local SRAM and Switchable SRAM. + * Only clears regions that are actually mapped from Device Tree. + * Matches upstream patterns (e.g., imx_rproc / ti_k3_r5_remoteproc) to: + * - Prevent ECC/parity noise on uninitialized memory banks. + * - Ensure NOBITS / .bss sections start strictly at zero. + * - Clear stale trace0 logs/telemetry from previous runs. + */ + if (priv->r_sram_va && priv->r_sram_size) + memset_io(priv->r_sram_va, 0, priv->r_sram_size); + + if (priv->r_sram1_va && priv->r_sram1_size) + memset_io(priv->r_sram1_va, 0, priv->r_sram1_size); + + return 0; + +err_disable_msgbox_clk: + if (priv->clk_msgbox) + clk_disable_unprepare(priv->clk_msgbox); +err_disable_sram_clk: + if (priv->clk_sram) + clk_disable_unprepare(priv->clk_sram); +err_disable_bus: + if (priv->clk_bus) + clk_disable_unprepare(priv->clk_bus); +err_disable_parent: + if (priv->clk_parent) + clk_disable_unprepare(priv->clk_parent); +err_assert_msgbox: + if (priv->rst_msgbox) + reset_control_assert(priv->rst_msgbox); +err_assert_sram: + if (priv->rst_sram) + reset_control_assert(priv->rst_sram); +err_assert_cfg: + if (priv->rst_cfg) + reset_control_assert(priv->rst_cfg); + return ret; +} + +#if IS_ENABLED(CONFIG_SUNXI_REMOTEPROC_KUNIT_TEST) +EXPORT_SYMBOL_GPL(sunxi_rproc_prepare); +#endif + +int sunxi_rproc_unprepare(struct rproc *rproc) +{ + struct sunxi_rproc *priv = rproc->priv; + const struct sunxi_rproc_cfg *cfg = priv->cfg ? priv->cfg : &sun55i_riscv_cfg; + + /* Symmetrical CCU unwinding */ + if (cfg->has_remap_reg && priv->remap_va) { + u32 remap_val = readl(priv->remap_va); + + remap_val &= ~SUNXI_REMAP_SRAMA3_2_BIT; + writel(remap_val, priv->remap_va); + } + + if (priv->clk_core) + clk_disable_unprepare(priv->clk_core); + + if (priv->clk_msgbox) + clk_disable_unprepare(priv->clk_msgbox); + + if (priv->clk_sram) + clk_disable_unprepare(priv->clk_sram); + + if (priv->clk_bus) + clk_disable_unprepare(priv->clk_bus); + + if (priv->clk_parent) + clk_disable_unprepare(priv->clk_parent); + + if (priv->rst_msgbox) + reset_control_assert(priv->rst_msgbox); + + if (priv->rst_sram) + reset_control_assert(priv->rst_sram); + + if (priv->rst_cfg) + reset_control_assert(priv->rst_cfg); + + return 0; +} + +#if IS_ENABLED(CONFIG_SUNXI_REMOTEPROC_KUNIT_TEST) +EXPORT_SYMBOL_GPL(sunxi_rproc_unprepare); +#endif + +int sunxi_rproc_start(struct rproc *rproc) +{ + struct sunxi_rproc *priv = rproc->priv; + const struct sunxi_rproc_cfg *cfg = priv->cfg ? priv->cfg : &sun55i_riscv_cfg; + int ret; + + dev_info(priv->dev, "Starting %s core at entry 0x%llx\n", + cfg->name ? cfg->name : "remote", (u64)rproc->bootaddr); + + if (rproc->bootaddr > U32_MAX) + return -EINVAL; + + /* Enable crash IRQ now that core is executing */ + if (priv->crash_irq > 0 && !priv->crash_irq_enabled) { + enable_irq(priv->crash_irq); + priv->crash_irq_enabled = true; + } + + /* + * Program boot vector while the core execution reset is held. + * The CFG block bus was un-gated during prepare() via rst_cfg. + */ + if (priv->cfg_va) { + writel((u32)rproc->bootaddr, priv->cfg_va + cfg->boot_reg_offset); + dev_dbg(priv->dev, "STA_ADD set to 0x%08x\n", (u32)rproc->bootaddr); + } + + /* Release core execution reset so the core begins execution at bootaddr */ + if (priv->rst_core) { + ret = reset_control_deassert(priv->rst_core); + if (ret) { + dev_err(priv->dev, "failed to release core reset: %d\n", ret); + return ret; + } + } else if (priv->rst_cfg) { + ret = reset_control_deassert(priv->rst_cfg); + if (ret) { + dev_err(priv->dev, "failed to release cfg reset: %d\n", ret); + return ret; + } + } + + return 0; +} + +#if IS_ENABLED(CONFIG_SUNXI_REMOTEPROC_KUNIT_TEST) +EXPORT_SYMBOL_GPL(sunxi_rproc_start); +#endif + +int sunxi_rproc_stop(struct rproc *rproc) +{ + struct sunxi_rproc *priv = rproc->priv; + const struct sunxi_rproc_cfg *cfg = priv->cfg ? priv->cfg : &sun55i_riscv_cfg; + + dev_info(priv->dev, "Halting %s core...\n", + cfg->name ? cfg->name : "remote"); + + /* + * Assert reset first so the core stops generating mailbox interrupts, + * then drain any work already queued. Reversing this order leaves a + * window where a late IRQ re-queues vq_work after cancel_work_sync() + * returns, executing on freed resources. + */ + if (priv->rst_core) + reset_control_assert(priv->rst_core); + else if (priv->rst_cfg) + reset_control_assert(priv->rst_cfg); + + /* Disable crash IRQ while core is stopped */ + if (priv->crash_irq > 0 && priv->crash_irq_enabled) { + disable_irq(priv->crash_irq); + priv->crash_irq_enabled = false; + } + + cancel_work_sync(&priv->vq_work); + + return 0; +} + +#if IS_ENABLED(CONFIG_SUNXI_REMOTEPROC_KUNIT_TEST) +EXPORT_SYMBOL_GPL(sunxi_rproc_stop); +#endif + +void sunxi_rproc_kick(struct rproc *rproc, int vqid) +{ + struct sunxi_rproc *priv = rproc->priv; + int ret; + + if (!priv->tx_chan) + return; + + /* + * Use priv->kick_msg rather than a stack-local variable. The mailbox + * controller runs with tx_block=false, so mbox_send_message() may + * queue the pointer and return before the hardware reads the message. + * A stack-local vqid would be a use-after-return at that point. + */ + priv->kick_msg = (u32)vqid; + ret = mbox_send_message(priv->tx_chan, &priv->kick_msg); + if (ret < 0) + dev_err_ratelimited(priv->dev, "failed to send mailbox kick: %d\n", ret); + + mbox_client_txdone(priv->tx_chan, 0); +} + +#if IS_ENABLED(CONFIG_SUNXI_REMOTEPROC_KUNIT_TEST) +EXPORT_SYMBOL_GPL(sunxi_rproc_kick); +#endif + +static int sunxi_rproc_da_to_sys(struct sunxi_rproc *priv, u64 da, + size_t len, u64 *sys, bool *is_iomem) +{ + const struct sunxi_rproc_cfg *cfg = priv->cfg ? priv->cfg : &sun55i_riscv_cfg; + size_t i; + + if (len == 0 || da > U64_MAX - len) + return -EINVAL; + + if (cfg->att) { + for (i = 0; i < cfg->att_size; i++) { + const struct sunxi_rproc_att *att = &cfg->att[i]; + + if (da >= att->da && (da + len) <= (att->da + att->size)) { + *sys = att->sa + (da - att->da); + if (is_iomem) + *is_iomem = !!(att->flags & ATT_IOMEM); + return 0; + } + } + } + + return -ENOENT; +} + +void *sunxi_rproc_da_to_va(struct rproc *rproc, u64 da, size_t len, bool *is_iomem) +{ + struct sunxi_rproc *priv = rproc->priv; + u64 sys; + + /* + * Reject zero length and any da+len combination that overflows u64. + * A crafted ELF with da near U64_MAX could wrap da+len to a small value, + * bypassing upper-bound checks and allowing arbitrary kernel memory + * to be mapped during firmware loading. + */ + if (len == 0 || da > U64_MAX - len) + return NULL; + + /* + * 1. Translate core-local device addresses (DA) to system bus + * addresses (Host PA) using the SoC address translation table (ATT). + */ + if (sunxi_rproc_da_to_sys(priv, da, len, &sys, is_iomem) == 0) { + if (priv->r_sram_va && sys >= priv->r_sram_phys && + (sys + len) <= (priv->r_sram_phys + priv->r_sram_size)) + return (__force void *)(priv->r_sram_va + (sys - priv->r_sram_phys)); + + if (priv->r_sram1_va && sys >= priv->r_sram1_phys && + (sys + len) <= (priv->r_sram1_phys + priv->r_sram1_size)) + return (__force void *)(priv->r_sram1_va + (sys - priv->r_sram1_phys)); + + if (priv->dram_va && sys >= priv->dram_phys && + (sys + len) <= (priv->dram_phys + priv->dram_size)) + return (__force void *)(priv->dram_va + (sys - priv->dram_phys)); + + if (priv->trace_va && sys >= priv->trace_phys && + (sys + len) <= (priv->trace_phys + priv->trace_size)) + return (__force void *)(priv->trace_va + (sys - priv->trace_phys)); + } + + /* + * 2. Device Tree Memory Regions (Trace buffer, DRAM carveout, or + * dynamically-assigned SRAM regions whose host PA is supplied via DT). + */ + if (priv->trace_va && da >= priv->trace_phys && + (da + len) <= (priv->trace_phys + priv->trace_size)) { + if (is_iomem) + *is_iomem = false; + return (__force void *)(priv->trace_va + (da - priv->trace_phys)); + } + + if (priv->dram_va && da >= priv->dram_phys && + (da + len) <= (priv->dram_phys + priv->dram_size)) { + if (is_iomem) + *is_iomem = false; + return (__force void *)(priv->dram_va + (da - priv->dram_phys)); + } + + if (priv->r_sram_va && da >= priv->r_sram_phys && + (da + len) <= (priv->r_sram_phys + priv->r_sram_size)) { + if (is_iomem) + *is_iomem = true; + return (__force void *)(priv->r_sram_va + (da - priv->r_sram_phys)); + } + + if (priv->r_sram1_va && da >= priv->r_sram1_phys && + (da + len) <= (priv->r_sram1_phys + priv->r_sram1_size)) { + if (is_iomem) + *is_iomem = true; + return (__force void *)(priv->r_sram1_va + (da - priv->r_sram1_phys)); + } + + /* + * Return NULL to delegate all DRAM carveouts (vrings, buffers, + * code/data placed in DDR) directly to the remoteproc core's + * internal carveout table. + */ + return NULL; +} + +#if IS_ENABLED(CONFIG_SUNXI_REMOTEPROC_KUNIT_TEST) +EXPORT_SYMBOL_GPL(sunxi_rproc_da_to_va); +#endif + +static int sunxi_rproc_parse_fw(struct rproc *rproc, const struct firmware *fw) +{ + int ret; + + ret = rproc_elf_load_rsc_table(rproc, fw); + if (ret == -EINVAL) { + dev_dbg(rproc->dev.parent, "no resource table found in ELF\n"); + return 0; + } + return ret; +} + +const struct rproc_ops sunxi_rproc_ops = { + .prepare = sunxi_rproc_prepare, + .unprepare = sunxi_rproc_unprepare, + .start = sunxi_rproc_start, + .stop = sunxi_rproc_stop, + .kick = sunxi_rproc_kick, + .da_to_va = sunxi_rproc_da_to_va, + .get_boot_addr = rproc_elf_get_boot_addr, + .load = rproc_elf_load_segments, + .parse_fw = sunxi_rproc_parse_fw, + .find_loaded_rsc_table = rproc_elf_find_loaded_rsc_table, + .sanity_check = rproc_elf_sanity_check, + .coredump = rproc_coredump, +}; + +#if IS_ENABLED(CONFIG_SUNXI_REMOTEPROC_KUNIT_TEST) +EXPORT_SYMBOL_GPL(sunxi_rproc_ops); +#endif + +static int sunxi_rproc_register_mem(struct platform_device *pdev, struct rproc *rproc) +{ + struct device *dev = &pdev->dev; + struct sunxi_rproc *priv = rproc->priv; + struct resource *res; + + /* 1. Map Optional RISC-V CFG Block ("cfg") */ + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cfg"); + if (res) { + priv->cfg_phys = res->start; + priv->cfg_va = devm_ioremap(dev, res->start, resource_size(res)); + if (!priv->cfg_va) + dev_warn(dev, "failed to map 'cfg' registers\n"); + } + + /* 2. Map Dedicated RISC-V Local SRAM Space 0 ("r_sram" or "sram") */ + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "r_sram"); + if (!res) + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sram"); + if (res) { + priv->r_sram_phys = res->start; + priv->r_sram_size = resource_size(res); + priv->r_sram_va = devm_ioremap_wc(dev, res->start, resource_size(res)); + if (!priv->r_sram_va) + return -ENOMEM; + } + + /* 3. Map Switchable RISC-V Local SRAM Space 1 ("r_sram1" / SRAMA3_2) - Optional */ + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "r_sram1"); + if (res) { + priv->r_sram1_phys = res->start; + priv->r_sram1_size = resource_size(res); + priv->r_sram1_va = devm_ioremap_wc(dev, res->start, resource_size(res)); + if (!priv->r_sram1_va) + dev_warn(dev, "failed to map 'r_sram1' resource\n"); + } + + /* 4. Map Remap Control Register ("remap" or "sram-for-cpux") - Optional */ + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "remap"); + if (!res) + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sram-for-cpux"); + if (res) { + priv->remap_phys = res->start; + if (resource_size(res) > SUNXI_REMAP_CTRL_OFFSET && + IS_ALIGNED(res->start, PAGE_SIZE)) { + void __iomem *base = devm_ioremap(dev, res->start, resource_size(res)); + + if (base) + priv->remap_va = base + SUNXI_REMAP_CTRL_OFFSET; + } else { + priv->remap_va = devm_ioremap(dev, res->start, resource_size(res)); + } + if (!priv->remap_va) + dev_warn(dev, "failed to map 'remap' register\n"); + } + + /* 4b. Map Boot DRAM Carveout (Resource "dram" if defined in reg) */ + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dram"); + if (res) { + priv->dram_phys = res->start; + priv->dram_size = resource_size(res); + priv->dram_va = devm_memremap(dev, res->start, resource_size(res), MEMREMAP_WB); + if (!priv->dram_va) + priv->dram_va = (__force void *)devm_ioremap_wc(dev, res->start, + resource_size(res)); + if (!priv->dram_va) + dev_warn(dev, "failed to map 'dram' resource\n"); + } + + dev_info(dev, "Memory resources: r_sram=%s, r_sram1=%s, remap=%s, cfg=%s, dram=%s\n", + priv->r_sram_va ? "yes" : "no", + priv->r_sram1_va ? "yes" : "no", + priv->remap_va ? "yes" : "no", + priv->cfg_va ? "yes" : "no", + priv->dram_va ? "yes" : "no"); + + /* 5. Map Trace Buffer (Resource "trace" if defined in reg) */ + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "trace"); + if (res) { + priv->trace_phys = res->start; + priv->trace_size = resource_size(res); + priv->trace_va = devm_memremap(dev, res->start, resource_size(res), MEMREMAP_WB); + if (!priv->trace_va) + priv->trace_va = (__force void *)devm_ioremap_wc(dev, res->start, + resource_size(res)); + dev_info(dev, "mapped 'trace' mmio resource %pa+%zu\n", + &priv->trace_phys, priv->trace_size); + } + + return 0; +} + +static int sunxi_rproc_parse_memory_regions(struct rproc *rproc) +{ + struct device *dev = rproc->dev.parent; + struct device_node *np = dev->of_node; + struct sunxi_rproc *priv = rproc->priv; + int num_rmems; + int i; + + if (!np) + return 0; + + num_rmems = of_count_phandle_with_args(np, "memory-region", NULL); + if (num_rmems <= 0) + return 0; + + /* Bind default DMA pool for dynamic allocations (e.g. vdev0 vrings/buffers) */ + if (of_reserved_mem_device_init(dev) == 0) + priv->has_reserved_mem = true; + else + dev_dbg(dev, "no dedicated DMA pool assigned from reserved-memory\n"); + + /* Register all reserved-memory regions as formal remoteproc carveouts */ + for (i = 0; i < num_rmems; i++) { + struct device_node *rmem_np; + struct resource res; + const char *name = NULL; + struct rproc_mem_entry *mem; + void *va; + + rmem_np = of_parse_phandle(np, "memory-region", i); + if (!rmem_np) + continue; + + if (of_address_to_resource(rmem_np, 0, &res)) { + of_node_put(rmem_np); + continue; + } + + of_property_read_string_index(np, "memory-region-names", i, &name); + if (!name) + name = rmem_np->name; + + if (name && (strstr(name, "trace") || of_node_name_eq(rmem_np, "trace"))) { + priv->trace_phys = res.start; + priv->trace_size = resource_size(&res); + priv->trace_va = devm_memremap(dev, res.start, resource_size(&res), + MEMREMAP_WB); + if (!priv->trace_va) + priv->trace_va = (__force void *) + devm_ioremap_wc(dev, res.start, resource_size(&res)); + dev_info(dev, "registered trace carveout %pa+%zu (%s)\n", + &priv->trace_phys, priv->trace_size, name); + } else if (name && (strstr(name, "dram") || strstr(name, "vram"))) { + priv->dram_phys = res.start; + priv->dram_size = resource_size(&res); + priv->dram_va = devm_memremap(dev, res.start, resource_size(&res), + MEMREMAP_WB); + if (!priv->dram_va) + priv->dram_va = (__force void *) + devm_ioremap_wc(dev, res.start, resource_size(&res)); + dev_info(dev, "registered dram carveout %pa+%zu (%s)\n", + &priv->dram_phys, priv->dram_size, name); + } + + /* Reuse existing SRAM mapping if region overlaps, else ioremap */ + if (priv->r_sram1_va && res.start == priv->r_sram1_phys) + va = (__force void *)priv->r_sram1_va; + else if (priv->r_sram_va && res.start == priv->r_sram_phys) + va = (__force void *)priv->r_sram_va; + else + va = (__force void *)devm_ioremap_wc(dev, res.start, resource_size(&res)); + + if (va) { + mem = rproc_mem_entry_init(dev, va, (dma_addr_t)res.start, + resource_size(&res), (u32)res.start, + NULL, NULL, "%s", name); + if (mem) { + mem->is_iomem = true; + rproc_add_carveout(rproc, mem); + } + } + of_node_put(rmem_np); + } + + return 0; +} + +static int sunxi_rproc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + const char *fw_name = "riscv-firmware.elf"; + struct sunxi_rproc *priv; + struct rproc *rproc; + int crash_irq; + int ret; + + of_property_read_string(dev->of_node, "firmware-name", &fw_name); + + rproc = devm_rproc_alloc(dev, dev_name(dev), &sunxi_rproc_ops, + fw_name, sizeof(*priv)); + if (!rproc) { + dev_err(dev, "failed to allocate rproc context\n"); + return -ENOMEM; + } + + ret = dma_set_coherent_mask(dev, DMA_BIT_MASK(32)); + if (ret) { + dev_err(dev, "failed to set 32-bit DMA coherent mask: %d\n", ret); + return ret; + } + + priv = rproc->priv; + priv->rproc = rproc; + priv->dev = dev; + priv->cfg = of_device_get_match_data(dev); + if (!priv->cfg) + priv->cfg = &sun55i_riscv_cfg; + + /* 1. Common Clock Framework (CCF) Clocks */ + priv->clk_parent = devm_clk_get_optional(dev, "parent"); + if (IS_ERR(priv->clk_parent)) + return dev_err_probe(dev, PTR_ERR(priv->clk_parent), + "failed to get 'parent' clock\n"); + + priv->clk_bus = devm_clk_get_optional(dev, "bus"); + if (IS_ERR(priv->clk_bus)) + return dev_err_probe(dev, PTR_ERR(priv->clk_bus), "failed to get 'bus' clock\n"); + + priv->clk_core = devm_clk_get_optional(dev, "core"); + if (IS_ERR(priv->clk_core)) + return dev_err_probe(dev, PTR_ERR(priv->clk_core), "failed to get 'core' clock\n"); + + priv->clk_sram = devm_clk_get_optional(dev, "sram"); + if (IS_ERR(priv->clk_sram)) + return dev_err_probe(dev, PTR_ERR(priv->clk_sram), "failed to get 'sram' clock\n"); + + priv->clk_msgbox = devm_clk_get_optional(dev, "msgbox"); + if (IS_ERR(priv->clk_msgbox)) + return dev_err_probe(dev, PTR_ERR(priv->clk_msgbox), + "failed to get 'msgbox' clock\n"); + + /* 2. Resets */ + priv->rst_core = devm_reset_control_get_optional_exclusive(dev, "core"); + if (IS_ERR(priv->rst_core)) + return dev_err_probe(dev, PTR_ERR(priv->rst_core), "failed to get 'core' reset\n"); + + priv->rst_cfg = devm_reset_control_get_optional_exclusive(dev, "cfg"); + if (IS_ERR(priv->rst_cfg)) + return dev_err_probe(dev, PTR_ERR(priv->rst_cfg), "failed to get 'cfg' reset\n"); + + priv->rst_sram = devm_reset_control_get_optional_exclusive(dev, "sram"); + if (IS_ERR(priv->rst_sram)) + return dev_err_probe(dev, PTR_ERR(priv->rst_sram), "failed to get 'sram' reset\n"); + + priv->rst_msgbox = devm_reset_control_get_optional_exclusive(dev, "msgbox"); + if (IS_ERR(priv->rst_msgbox)) + return dev_err_probe(dev, PTR_ERR(priv->rst_msgbox), + "failed to get 'msgbox' reset\n"); + + /* 3. Memory Windows (TCM, SRAM, CFG) */ + ret = sunxi_rproc_register_mem(pdev, rproc); + if (ret) + return ret; + + /* 4. Dynamic Reserved Memory Carveouts & DMA Pools from Device Tree */ + ret = sunxi_rproc_parse_memory_regions(rproc); + if (ret) + return ret; + + /* 5. Optional Hardware Crash Notification IRQ */ + crash_irq = platform_get_irq_byname_optional(pdev, "crash"); + if (crash_irq > 0) { + ret = devm_request_threaded_irq(dev, crash_irq, NULL, + sunxi_rproc_crash_handler, + IRQF_ONESHOT | IRQF_NO_AUTOEN, + "sunxi-rproc-crash", + priv); + if (ret) { + dev_warn(dev, "failed to request crash IRQ %d: %d\n", crash_irq, ret); + } else { + priv->crash_irq = crash_irq; + priv->crash_irq_enabled = false; + } + } + + /* 6. Mailbox IPC Client */ + INIT_WORK(&priv->vq_work, sunxi_rproc_vq_work); + + priv->cl.dev = dev; + priv->cl.rx_callback = sunxi_rproc_mb_rx_callback; + priv->cl.tx_block = false; + priv->cl.knows_txdone = true; + + /* + * If the hardware mailbox is assigned to userspace (generic-uio) or + * lacks #mbox-cells, run RemoteProc in standalone mode. + */ + if (dev->of_node) { + struct device_node *mb_node = of_parse_phandle(dev->of_node, "mboxes", 0); + + if (mb_node) { + if (of_device_is_compatible(mb_node, "generic-uio") || + !of_property_read_bool(mb_node, "#mbox-cells")) { + dev_info(dev, "Mailbox assigned to UIO; running standalone mode\n"); + of_node_put(mb_node); + goto skip_mbox; + } + of_node_put(mb_node); + } + } + + priv->tx_chan = mbox_request_channel_byname(&priv->cl, "tx"); + if (IS_ERR(priv->tx_chan)) { + if (PTR_ERR(priv->tx_chan) == -EPROBE_DEFER) { + ret = -EPROBE_DEFER; + goto err_mem_release; + } + dev_info(dev, "no tx mailbox channel configured; running standalone mode\n"); + priv->tx_chan = NULL; + } + + if (priv->tx_chan) { + priv->rx_chan = mbox_request_channel_byname(&priv->cl, "rx"); + if (IS_ERR(priv->rx_chan)) { + if (PTR_ERR(priv->rx_chan) == -EPROBE_DEFER) { + ret = -EPROBE_DEFER; + goto err_mbox_release; + } + dev_info(dev, "no rx mailbox channel configured\n"); + priv->rx_chan = NULL; + } + } + +skip_mbox: + platform_set_drvdata(pdev, rproc); + + ret = rproc_add(rproc); + if (ret) { + dev_err(dev, "failed to register rproc device: %d\n", ret); + goto err_mbox_release; + } + + dev_info(dev, "Allwinner %s remoteproc registered (%s)\n", + priv->cfg ? priv->cfg->name : "remote", fw_name); + return 0; + +err_mbox_release: + cancel_work_sync(&priv->vq_work); + /* + * mbox_request_channel_byname() can return ERR_PTR on failure. + * Guard with IS_ERR() to avoid calling mbox_free_channel() with + * an invalid pointer, which would panic on the first dereference. + */ + if (priv->rx_chan && !IS_ERR(priv->rx_chan)) + mbox_free_channel(priv->rx_chan); + if (priv->tx_chan && !IS_ERR(priv->tx_chan)) + mbox_free_channel(priv->tx_chan); +err_mem_release: + if (priv->has_reserved_mem) + of_reserved_mem_device_release(dev); + return ret; +} + +static void sunxi_rproc_remove(struct platform_device *pdev) +{ + struct rproc *rproc = platform_get_drvdata(pdev); + struct sunxi_rproc *priv = rproc->priv; + + /* + * Teardown order is critical: + * 1. Disable crash IRQ first so late hardware crash alerts cannot + * race against rproc_del() or report crashes on a deleted device. + * 2. rproc_del() stops the remote core and tears down VirtIO/vring, + * which stops the hardware from generating further mailbox IRQs. + * 3. cancel_work_sync() drains any in-flight vq_work. Calling this + * before rproc_del() risks a late RX IRQ re-queuing work after + * cancel_work_sync() returns, executing on freed priv->rx_chan. + * 4. Free mailbox channels only after the workqueue is fully drained. + */ + if (priv->crash_irq > 0 && priv->crash_irq_enabled) { + disable_irq(priv->crash_irq); + priv->crash_irq_enabled = false; + } + + rproc_del(rproc); + + if (priv->rx_chan) { + mbox_free_channel(priv->rx_chan); + priv->rx_chan = NULL; + } + if (priv->tx_chan) { + mbox_free_channel(priv->tx_chan); + priv->tx_chan = NULL; + } + + cancel_work_sync(&priv->vq_work); + + if (priv->has_reserved_mem) + of_reserved_mem_device_release(&pdev->dev); +} + +static const struct of_device_id sunxi_rproc_of_match[] = { + /* + * A523, A527, and T527 are the same silicon die (sun55i family). + * Use a single compatible string per upstream DT binding policy. + */ + { .compatible = "allwinner,sun55i-a523-rproc", .data = &sun55i_riscv_cfg }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, sunxi_rproc_of_match); + +static struct platform_driver sunxi_rproc_driver = { + .probe = sunxi_rproc_probe, + .remove = sunxi_rproc_remove, + .driver = { + .name = DRIVER_NAME, + .of_match_table = sunxi_rproc_of_match, + }, +}; +module_platform_driver(sunxi_rproc_driver); + +MODULE_AUTHOR("Allwinner Technology Co., Ltd."); +MODULE_AUTHOR("Tim Michals "); +MODULE_DESCRIPTION("Allwinner XuanTie E906/E907 RISC-V Remoteproc Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/remoteproc/sunxi_rproc.h b/drivers/remoteproc/sunxi_rproc.h new file mode 100644 index 000000000000..5b516b5ac956 --- /dev/null +++ b/drivers/remoteproc/sunxi_rproc.h @@ -0,0 +1,116 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef _SUNXI_RPROC_H_ +#define _SUNXI_RPROC_H_ + +#include +#include +#include +#include +#include +#include + +/* XuanTie E906/E907 core-local view of dedicated SRAM Spaces (Allwinner A523/A527/T527) */ +#define E907_SRAM_C_DA 0x00020000UL +#define E907_SRAM_SPACE0_DA 0x3ff80000UL +#define E907_SRAM_SPACE0_DA_ALT 0x3ffc0000UL +#define E907_SRAM_SPACE1_DA 0x40000000UL +#define E907_SRAM_SPACE1_DA_ALT 0x40040000UL + +/* Allwinner A523/A527/T527 System Bus (Host Physical) Addresses & Window Sizes */ +#define SUN55I_SRAM_SPACE0_SYS 0x07280000UL +#define SUN55I_SRAM_SPACE0_SIZE 0x00040000UL /* 256 KB */ +#define SUN55I_SRAM_SPACE1_SYS 0x072c0000UL +#define SUN55I_SRAM_SPACE1_SIZE 0x00040000UL /* 256 KB */ + +/* Address Translation Table flags */ +#define ATT_IOMEM BIT(30) + +struct sunxi_rproc_att { + u64 da; + u64 sa; + size_t size; + int flags; +}; + +/* XuanTie CFG Block Register Offsets */ +#define E906_CTRL_REG 0x0000 +#define E906_STA_ADD_REG 0x0204 + +/* Remap Control Register (offset 0x364 in PRCM_R_CCU / MCU_CCU) */ +#define SUNXI_REMAP_CTRL_OFFSET 0x0364 +/* Bit 0: 0 = local RAM for MCU; 1 = share for system */ +#define SUNXI_REMAP_MCU_RAM_BIT BIT(0) +/* Bit 1: 0 = SRAMA3_2 not shared; 1 = share for MCU_SYS */ +#define SUNXI_REMAP_SRAMA3_2_BIT BIT(1) + +struct sunxi_rproc_cfg { + const char *name; + const struct sunxi_rproc_att *att; + size_t att_size; + bool has_remap_reg; + u32 boot_reg_offset; +}; + +extern const struct sunxi_rproc_cfg sun55i_riscv_cfg; + +struct sunxi_rproc { + struct rproc *rproc; + struct device *dev; + const struct sunxi_rproc_cfg *cfg; + + /* CCU Clocks & Resets */ + struct clk *clk_parent; + struct clk *clk_bus; + struct clk *clk_core; + struct clk *clk_sram; + struct clk *clk_msgbox; + struct reset_control *rst_cfg; + struct reset_control *rst_core; + struct reset_control *rst_sram; + struct reset_control *rst_msgbox; + + /* Hardware Memory Windows (Dedicated SRAM, Switchable SRAM, Remap) */ + void __iomem *cfg_va; + phys_addr_t cfg_phys; + + void __iomem *remap_va; + phys_addr_t remap_phys; + + void __iomem *r_sram_va; + phys_addr_t r_sram_phys; + size_t r_sram_size; + + void __iomem *r_sram1_va; + phys_addr_t r_sram1_phys; + size_t r_sram1_size; + + void *dram_va; + phys_addr_t dram_phys; + size_t dram_size; + + /* Trace / DDR Reserved Memory Window */ + void *trace_va; + phys_addr_t trace_phys; + size_t trace_size; + + /* Reserved Memory & Mailbox State */ + bool has_reserved_mem; + int crash_irq; + bool crash_irq_enabled; + struct mbox_client cl; + struct mbox_chan *tx_chan; + struct mbox_chan *rx_chan; + struct work_struct vq_work; + u32 kick_msg; +}; + +extern const struct rproc_ops sunxi_rproc_ops; + +int sunxi_rproc_prepare(struct rproc *rproc); +int sunxi_rproc_unprepare(struct rproc *rproc); +int sunxi_rproc_start(struct rproc *rproc); +int sunxi_rproc_stop(struct rproc *rproc); +void sunxi_rproc_kick(struct rproc *rproc, int vqid); +void *sunxi_rproc_da_to_va(struct rproc *rproc, u64 da, size_t len, bool *is_iomem); + +#endif /* _SUNXI_RPROC_H_ */ From patchwork Sun Sep 27 00:20:15 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Tim Michals X-Patchwork-Id: 3494 Return-Path: X-Original-To: noreply@patchwork.local Delivered-To: noreply@patchwork.local Received: from sea.lore.kernel.org (sea.lore.kernel.org [172.234.253.10]) by mxe881.netcup.net (Postfix) with ESMTPS id CC0581C1033 for ; Sun, 27 Sep 2026 02:23:03 +0200 (CEST) Authentication-Results: mxe881; dkim=pass header.d=gmail.com; spf=pass (sender IP is 172.234.253.10) smtp.mailfrom=linux-sunxi+bounces-26296-noreply=patchwork.local@lists.linux.dev smtp.helo=sea.lore.kernel.org Received-SPF: pass (mxe881: domain of lists.linux.dev designates 172.234.253.10 as permitted sender) client-ip=172.234.253.10; envelope-from=linux-sunxi+bounces-26296-noreply=patchwork.local@lists.linux.dev; helo=sea.lore.kernel.org; Received: from smtp.subspace.kernel.org (conduit.subspace.kernel.org [100.90.174.1]) by sea.lore.kernel.org (Postfix) with ESMTP id C9C7A35030 for ; Sun, 27 Sep 2026 00:21:07 +0000 (UTC) Received: from localhost.localdomain (localhost.localdomain [127.0.0.1]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 91AD6236453; Sun, 27 Sep 2026 00:21:07 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="cVCHs3my" X-Original-To: linux-sunxi@lists.linux.dev Received: from mail-oo2-f36.google.com (mail-oo2-f36.google.com [74.125.231.164]) (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 235282459C5 for ; Sun, 27 Sep 2026 00:21:03 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.231.164 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790468467; cv=none; b=udJ2I4FC2u4Uf89YzAHT1jtY3ZiN/iBpMyvjCkKYdKNwNaMLS1iJsue3J/+qjFMFzCoRaBaY9RsE57UspY+56OCyeJfaPS9gj8dgoHVvKHOJZQ1IjFy8W4JAmn4ekQKQE/0FIu69WJ4yEaJNZWKmIs/olniFDgQkSSJzRiC2prw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790468467; c=relaxed/simple; bh=coOUM27gfafkQv+UeAD7uM/F57Ajvo6nw91IJ+SpC3Y=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=GjpMvBBwSFDqUePyA4Zegni69tBP4Yvzj0D4/Y1fCAN2pqcaAhOXrKz4n0hZ2SXUaHAg1hiQVlCMRuxgdNLyttIyg05GyCAdWyn6ZiFFEIOcCzpfAvD2l+zEofs4e55fiUp2bxyGb17xa1fsECZcMlAqB5iSizGpKDTD4UYuam8= 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=cVCHs3my; arc=none smtp.client-ip=74.125.231.164 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 Received: by mail-oo2-f36.google.com with SMTP id 006d021491bc7-6d77b3455bbso326143eaf.0 for ; Sat, 26 Sep 2026 17:21:03 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1790468462; x=1791073262; darn=lists.linux.dev; h=content-transfer-encoding:content-type: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=crIEM7P/OeuXHIgUQCdX4agzK/kh0va6lRx1clVdQD8=; b=cVCHs3mytnZvga0Mjt6K0QHwPNuUA1TAH2gh1X1E+XuLYtv/2IuiRRBgDdp4weZNqd 6aextq9573KmYF/dJWeq1BWYuAMCaHQTNmZd3+F+aAbpnZ/mJE0iO0cf4tXIY3ZHd3N7 3SeZ2M3xhow6gjnfMrATVfO1OsEO/qUHTAZkJoEyc0Yrqk6rQYEltijhWoPWDDGNuuZJ NQAXbuUKuvaxfiKAaoMBMzIokuOT3vHbs8dF0yKelOw2ceIB4D1RdkQOUdLpUZlLVGb5 c2JDnCeh9DbdwmRTl2MPjAg6vyZ+yXTAf74ALJmtZ9g2KtSSX3D3iNfcZvrLHtEl5Gsi shyQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1790468462; x=1791073262; h=content-transfer-encoding:content-type: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=crIEM7P/OeuXHIgUQCdX4agzK/kh0va6lRx1clVdQD8=; b=d1PNDLbTS6iDPH9kCYJBRVKORBeCNSk+q/hocwxcENNVew2g2DwgosYOdzzcrJ/fo6 4md5OkXK+Sy0SrB1ZMn/Idg1zcumTtlGxB/hPAuHwFuC2bm7QYeeMyH1YIDKCiGwlpSS 2idoIQkToMBzIuv7Kjk9T+OMdkw+5/CuhjD0azQHtE92fdz1LQrVrqqFJs7DTMRpG47O uu0wWaeFoyRaMoPsO7i8K1BhDHkSnD9E149gYGmKUktn/ShLFeLMgEFIRstNGJiazDsX twNgVIQL0FXwgYIoW8hqZOyJjq9/i4ZInrMUTT9I5IM7EnJD+xMqVycQApZLEa++oC8C JwRg== X-Gm-Message-State: AFuF++llA1ILvrjGuvQz/wFjYLPEoiMi9HJ6h2pkAek0DRQjCv4/P79o wIWT/Rl06WDu1VYxdIjMu0mE6YINn2ojn5KprAiAntIX0qpTcFl1+WdbcHWVklMJ X-Gm-Gg: AYBFou3DftEFpRIiUOZ2okz5nIuInLrnwYV+pgEf+x2Sxv4EZs5uju8eA32rmCIwjbE FQfDpZIiqoKQkhtUxOOIonYZWruKrcHHgs+cZzzsm48S2anwUnZsepn69iUXRwa8ue5ez5v7rQ7 eMt5sazPUB0PVZvkIGUlfv+Z+UzdaONGX1MzDX1/RSB4h688Pj/GI8QKeXTJ+mzoZe81wg9Fuzi yW1x0uQcWWFbFIxYnGRp4KoAJzTFq0lGXJh61hjNc6t1qeHr9tLnpDYyHZOiuQmek4arYr8OHIf W22CUooPF+Gu78iOeWhgEaKCRsBxH80Pp7Z/hMXsyDiihGOpXebVImueOr47hJ/mlPZfcSNjbXT ryGUhvclW1iqBgnIrLs1SNS7BvRjwXYxBEaeb1TvYwJ485FDML0CjdWNvAW5Bk4GgYTEMgGnjOq F014JcxbKviyHMYbIZz0DU3R8+qrYKIaciJZHRP6KT6jxRH71wLuxLZ/3wN1DXTnJrZtwDLw26y rDXS5pNNe+pwATEtCa2MQ== X-Received: by 2002:a4a:ee19:0:b0:6b1:95fb:81e with SMTP id 006d021491bc7-6d43f2c6fbfmr9936857eaf.22.1790468461837; Sat, 26 Sep 2026 17:21:01 -0700 (PDT) Received: from tcmichals-5759.lan ([2607:fb91:3584:35a9:66f8:2cb5:aa80:21fd]) by smtp.gmail.com with ESMTPSA id 006d021491bc7-6d8828e1ea0sm838989eaf.6.2026.09.26.17.20.59 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 26 Sep 2026 17:21:01 -0700 (PDT) From: Tim Michals To: linux-sunxi@lists.linux.dev, linux-remoteproc@vger.kernel.org Cc: devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, wens@kernel.org, jernej.skrabec@gmail.com, samuel@sholland.org, andersson@kernel.org, mathieu.poirier@linaro.org, jassisinghbrar@gmail.com, robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org, Tim Michals Subject: [PATCH v2 6/7] remoteproc: sunxi: add KUnit test suite for sunxi remoteproc driver Date: Sat, 26 Sep 2026 19:20:15 -0500 Message-ID: <20260927002021.797069-7-tcmichals@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260927002021.797069-1-tcmichals@gmail.com> References: <20260922034711.190253-1-tcmichals@gmail.com> <20260927002021.797069-1-tcmichals@gmail.com> Precedence: bulk X-Mailing-List: linux-sunxi@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Rspamd-Server: rspamd-worker-8404 X-Spamd-Result: default: False [-0.16 / 15.00]; BAYES_HAM(-5.50)[100.00%]; RBL_SENDERSCORE(2.00)[172.234.253.10:from]; SUSPICIOUS_RECIPS(1.50)[]; MID_CONTAINS_FROM(1.00)[]; DMARC_POLICY_SOFTFAIL(1.00)[gmail.com : SPF not aligned (relaxed), No valid DKIM,none]; MAILLIST(-0.15)[generic]; BAD_REP_POLICIES(0.10)[]; MIME_GOOD(-0.10)[text/plain]; HAS_LIST_UNSUB(-0.01)[]; RCPT_COUNT_TWELVE(0.00)[15]; DBL_BLOCKED_OPENRESOLVER(0.00)[kunit.py:url,sea.lore.kernel.org:rdns,sea.lore.kernel.org:helo]; TO_DN_SOME(0.00)[]; FREEMAIL_FROM(0.00)[gmail.com]; FORGED_SENDER_MAILLIST(0.00)[]; FREEMAIL_CC(0.00)[vger.kernel.org,lists.infradead.org,kernel.org,gmail.com,sholland.org,linaro.org]; FORGED_RECIPIENTS_MAILLIST(0.00)[]; TAGGED_RCPT(0.00)[dt]; FROM_HAS_DN(0.00)[]; ASN(0.00)[asn:63949, ipnet:172.234.224.0/19, country:SG]; RCVD_TLS_LAST(0.00)[]; MIME_TRACE(0.00)[0:+]; PRECEDENCE_BULK(0.00)[]; TAGGED_FROM(0.00)[bounces-26296-noreply=patchwork.local]; FROM_NEQ_ENVFROM(0.00)[tcmichals@gmail.com,linux-sunxi@lists.linux.dev]; ARC_ALLOW(0.00)[subspace.kernel.org:s=arc-20240116:i=1]; R_SPF_ALLOW(0.00)[+ip4:172.234.253.10]; RCVD_COUNT_FIVE(0.00)[6]; RCVD_VIA_SMTP_AUTH(0.00)[] X-Rspamd-Queue-Id: CC0581C1033 X-MORS-Enabled: yes X-MORS-DOMAIN: patchwork.local X-MORS-HOSTING: hosting172546 X-MORS-USER: hosting172546 X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= Add comprehensive in-kernel KUnit unit test suite (34 tests) for the Allwinner sunxi remoteproc driver. Tests validate: - da_to_va() memory window address translation and exact boundary limits across dedicated SRAM Space 0, SRAM Space 1, DRAM carveout, and trace. - 64-bit integer wraparound guards and negative unmapped region rejection. - Corrupted ELF segment overflow and malformed resource table entry rejection. - Mock MMIO start/stop lifecycle and boot address register programming. - Symmetrical prepare/unprepare SRAM remap bit manipulation and memory clearing. - Safe kick handling when transmit mailbox channels are unconfigured. Signed-off-by: Tim Michals --- drivers/remoteproc/Kconfig | 10 + drivers/remoteproc/Makefile | 1 + drivers/remoteproc/sunxi_rproc_test.c | 701 ++++++++++++++++++++++++++ 3 files changed, 712 insertions(+) create mode 100644 drivers/remoteproc/sunxi_rproc_test.c diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig index d35b033b9106..c2bddfd95566 100644 --- a/drivers/remoteproc/Kconfig +++ b/drivers/remoteproc/Kconfig @@ -396,6 +396,16 @@ config SUNXI_REMOTEPROC co-processor on Allwinner A523, A527, and T527 SoCs via the remote processor framework. +config SUNXI_REMOTEPROC_KUNIT_TEST + tristate "KUnit tests for Allwinner sunxi remoteproc" if !KUNIT_ALL_TESTS + depends on KUNIT + default KUNIT_ALL_TESTS + help + KUnit tests for the Allwinner sunxi_rproc driver, covering + da_to_va() address translation logic and boundary conditions + across dedicated SRAM spaces, DRAM carveouts, and trace regions. + Say Y here to run these tests during boot or via kunit.py. + endif # REMOTEPROC endmenu diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile index 02acefa6d880..a3503a71f332 100644 --- a/drivers/remoteproc/Makefile +++ b/drivers/remoteproc/Makefile @@ -42,3 +42,4 @@ obj-$(CONFIG_TI_K3_M4_REMOTEPROC) += ti_k3_m4_remoteproc.o ti_k3_common.o obj-$(CONFIG_TI_K3_R5_REMOTEPROC) += ti_k3_r5_remoteproc.o ti_k3_common.o obj-$(CONFIG_XLNX_R5_REMOTEPROC) += xlnx_r5_remoteproc.o obj-$(CONFIG_SUNXI_REMOTEPROC) += sunxi_rproc.o +obj-$(CONFIG_SUNXI_REMOTEPROC_KUNIT_TEST) += sunxi_rproc_test.o diff --git a/drivers/remoteproc/sunxi_rproc_test.c b/drivers/remoteproc/sunxi_rproc_test.c new file mode 100644 index 000000000000..2546f850f30b --- /dev/null +++ b/drivers/remoteproc/sunxi_rproc_test.c @@ -0,0 +1,701 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * KUnit tests for Allwinner XuanTie RISC-V remoteproc driver (sunxi_rproc.c) + * + * Comprehensive test suite validating: + * - da_to_va() address translation across all hardware windows & aliases + * - 64-bit integer overflow protection and zero-length handling + * - Out-of-bounds, cross-space isolation, and window unmapped error paths + * - Lifecycle operations: start() STA_ADD programming & bootaddr validation + * - Lifecycle operations: prepare() and unprepare() SRAM remap & clearing + * - kick() message formatting and NULL tx_chan safety + * - rproc_ops table completeness + * + * Directly tests driver functions without code duplication. + * + * Copyright (C) 2026 Tim Michals + */ + +#include +#include +#include +#include +#include "remoteproc_internal.h" +#include "sunxi_rproc.h" + +/* + * Fake VA addresses — sentinel pointers used to verify base + offset arithmetic + * without requiring real ioremap MMIO allocations. + */ +#define FAKE_SRAM_PTR ((void *)0xA0000000UL) +#define FAKE_SRAM1_PTR ((void *)0xB0000000UL) +#define FAKE_SRAM_VA ((void __force __iomem *)FAKE_SRAM_PTR) +#define FAKE_SRAM1_VA ((void __force __iomem *)FAKE_SRAM1_PTR) +#define FAKE_DRAM_VA ((void *)0xC0000000UL) +#define FAKE_TRACE_VA ((void *)0xD0000000UL) + +/* Standard A527 hardware parameters */ +#define A527_SRAM_PHYS SUN55I_SRAM_SPACE0_SYS +#define A527_SRAM_SIZE SUN55I_SRAM_SPACE0_SIZE +#define A527_SRAM1_PHYS SUN55I_SRAM_SPACE1_SYS +#define A527_SRAM1_SIZE SUN55I_SRAM_SPACE1_SIZE +#define A527_DRAM_PHYS 0x48000000ULL +#define A527_DRAM_SIZE 0x100000 /* 1 MB */ +#define A527_TRACE_PHYS 0x50000000ULL +#define A527_TRACE_SIZE 0x1000 /* 4 KB */ + +struct test_context { + struct rproc rproc; + struct sunxi_rproc priv; + u32 mock_cfg_regs[0x400 / 4]; + u32 mock_remap_reg; + u8 mock_sram_buf[1024]; + u8 mock_sram1_buf[1024]; +}; + +static void mock_vq_work(struct work_struct *work) +{ +} + +static struct test_context *create_test_ctx(struct kunit *test) +{ + struct test_context *ctx; + + ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); + + ctx->rproc.priv = &ctx->priv; + ctx->priv.rproc = &ctx->rproc; + + ctx->priv.r_sram_va = FAKE_SRAM_VA; + ctx->priv.r_sram_phys = A527_SRAM_PHYS; + ctx->priv.r_sram_size = A527_SRAM_SIZE; + + ctx->priv.r_sram1_va = FAKE_SRAM1_VA; + ctx->priv.r_sram1_phys = A527_SRAM1_PHYS; + ctx->priv.r_sram1_size = A527_SRAM1_SIZE; + + ctx->priv.dram_va = FAKE_DRAM_VA; + ctx->priv.dram_phys = A527_DRAM_PHYS; + ctx->priv.dram_size = A527_DRAM_SIZE; + + ctx->priv.trace_va = FAKE_TRACE_VA; + ctx->priv.trace_phys = A527_TRACE_PHYS; + ctx->priv.trace_size = A527_TRACE_SIZE; + ctx->priv.cfg = &sun55i_riscv_cfg; + + INIT_WORK(&ctx->priv.vq_work, mock_vq_work); + + return ctx; +} + +/* ==================== da_to_va: Basic & Guard Tests ==================== */ + +static void test_da_to_va_zero_length_returns_null(struct kunit *test) +{ + struct test_context *ctx = create_test_ctx(test); + + KUNIT_EXPECT_NULL(test, + sunxi_rproc_da_to_va(&ctx->rproc, + E907_SRAM_SPACE0_DA_ALT, 0, NULL)); +} + +static void test_da_to_va_overflow_guard(struct kunit *test) +{ + struct test_context *ctx = create_test_ctx(test); + + KUNIT_EXPECT_NULL(test, sunxi_rproc_da_to_va(&ctx->rproc, U64_MAX - 0x10, 0x20, NULL)); + KUNIT_EXPECT_NULL(test, sunxi_rproc_da_to_va(&ctx->rproc, U64_MAX, 1, NULL)); +} + +static void test_da_to_va_null_is_iomem_safe(struct kunit *test) +{ + struct test_context *ctx = create_test_ctx(test); + void *va; + + /* Must succeed without dereferencing NULL is_iomem */ + va = sunxi_rproc_da_to_va(&ctx->rproc, E907_SRAM_SPACE0_DA_ALT, 0x100, NULL); + KUNIT_ASSERT_NOT_NULL(test, va); + KUNIT_EXPECT_PTR_EQ(test, va, FAKE_SRAM_PTR); +} + +/* ==================== da_to_va: Space 0 Translations ==================== */ + +static void test_da_to_va_sram_space0_core_da(struct kunit *test) +{ + struct test_context *ctx = create_test_ctx(test); + bool is_iomem = false; + void *va; + + va = sunxi_rproc_da_to_va(&ctx->rproc, E907_SRAM_SPACE0_DA_ALT, 0x100, &is_iomem); + KUNIT_ASSERT_NOT_NULL(test, va); + KUNIT_EXPECT_PTR_EQ(test, va, FAKE_SRAM_PTR); + KUNIT_EXPECT_TRUE(test, is_iomem); +} + +static void test_da_to_va_sram_space0_core_da_offset(struct kunit *test) +{ + struct test_context *ctx = create_test_ctx(test); + void *va; + + va = sunxi_rproc_da_to_va(&ctx->rproc, E907_SRAM_SPACE0_DA_ALT + 0x1000, 0x100, NULL); + KUNIT_ASSERT_NOT_NULL(test, va); + KUNIT_EXPECT_PTR_EQ(test, va, FAKE_SRAM_PTR + 0x1000); +} + +static void test_da_to_va_sram_space0_host_phys(struct kunit *test) +{ + struct test_context *ctx = create_test_ctx(test); + bool is_iomem = false; + void *va; + + va = sunxi_rproc_da_to_va(&ctx->rproc, A527_SRAM_PHYS, 0x100, &is_iomem); + KUNIT_ASSERT_NOT_NULL(test, va); + KUNIT_EXPECT_PTR_EQ(test, va, FAKE_SRAM_PTR); + KUNIT_EXPECT_TRUE(test, is_iomem); +} + +static void test_da_to_va_sram_space0_host_phys_offset(struct kunit *test) +{ + struct test_context *ctx = create_test_ctx(test); + void *va; + + va = sunxi_rproc_da_to_va(&ctx->rproc, A527_SRAM_PHYS + 0x2000, 0x100, NULL); + KUNIT_ASSERT_NOT_NULL(test, va); + KUNIT_EXPECT_PTR_EQ(test, va, FAKE_SRAM_PTR + 0x2000); +} + +static void test_da_to_va_sram_space0_alt_3ff80000(struct kunit *test) +{ + struct test_context *ctx = create_test_ctx(test); + bool is_iomem = false; + void *va; + + va = sunxi_rproc_da_to_va(&ctx->rproc, E907_SRAM_SPACE0_DA, 0x100, &is_iomem); + KUNIT_ASSERT_NOT_NULL(test, va); + KUNIT_EXPECT_PTR_EQ(test, va, FAKE_SRAM_PTR); + KUNIT_EXPECT_TRUE(test, is_iomem); +} + +static void test_da_to_va_sram_space0_pubsram_c_da(struct kunit *test) +{ + struct test_context *ctx = create_test_ctx(test); + bool is_iomem = false; + void *va; + + va = sunxi_rproc_da_to_va(&ctx->rproc, E907_SRAM_C_DA, 0x100, &is_iomem); + KUNIT_ASSERT_NOT_NULL(test, va); + KUNIT_EXPECT_PTR_EQ(test, va, FAKE_SRAM_PTR); + KUNIT_EXPECT_TRUE(test, is_iomem); +} + +/* ==================== da_to_va: Space 1 Translations ==================== */ + +static void test_da_to_va_sram_space1_host_phys(struct kunit *test) +{ + struct test_context *ctx = create_test_ctx(test); + bool is_iomem = false; + void *va; + + va = sunxi_rproc_da_to_va(&ctx->rproc, A527_SRAM1_PHYS, 0x100, &is_iomem); + KUNIT_ASSERT_NOT_NULL(test, va); + KUNIT_EXPECT_PTR_EQ(test, va, FAKE_SRAM1_PTR); + KUNIT_EXPECT_TRUE(test, is_iomem); +} + +static void test_da_to_va_sram_space1_40000000(struct kunit *test) +{ + struct test_context *ctx = create_test_ctx(test); + bool is_iomem = false; + void *va; + + va = sunxi_rproc_da_to_va(&ctx->rproc, E907_SRAM_SPACE1_DA, 0x100, &is_iomem); + KUNIT_ASSERT_NOT_NULL(test, va); + KUNIT_EXPECT_PTR_EQ(test, va, FAKE_SRAM1_PTR); + KUNIT_EXPECT_TRUE(test, is_iomem); +} + +static void test_da_to_va_sram_space1_40040000(struct kunit *test) +{ + struct test_context *ctx = create_test_ctx(test); + bool is_iomem = false; + void *va; + + va = sunxi_rproc_da_to_va(&ctx->rproc, E907_SRAM_SPACE1_DA_ALT, 0x100, &is_iomem); + KUNIT_ASSERT_NOT_NULL(test, va); + KUNIT_EXPECT_PTR_EQ(test, va, FAKE_SRAM1_PTR); + KUNIT_EXPECT_TRUE(test, is_iomem); +} + +/* ==================== da_to_va: DRAM & Trace Translations ==================== */ + +static void test_da_to_va_dram_direct(struct kunit *test) +{ + struct test_context *ctx = create_test_ctx(test); + bool is_iomem = true; + void *va; + + va = sunxi_rproc_da_to_va(&ctx->rproc, A527_DRAM_PHYS, 0x100, &is_iomem); + KUNIT_ASSERT_NOT_NULL(test, va); + KUNIT_EXPECT_PTR_EQ(test, va, FAKE_DRAM_VA); + KUNIT_EXPECT_FALSE(test, is_iomem); +} + +static void test_da_to_va_trace_direct(struct kunit *test) +{ + struct test_context *ctx = create_test_ctx(test); + bool is_iomem = true; + void *va; + + va = sunxi_rproc_da_to_va(&ctx->rproc, A527_TRACE_PHYS, 0x100, &is_iomem); + KUNIT_ASSERT_NOT_NULL(test, va); + KUNIT_EXPECT_PTR_EQ(test, va, FAKE_TRACE_VA); + KUNIT_EXPECT_FALSE(test, is_iomem); +} + +/* ==================== da_to_va: Negative & Isolation Tests ==================== */ + +static void test_da_to_va_out_of_range(struct kunit *test) +{ + struct test_context *ctx = create_test_ctx(test); + + KUNIT_EXPECT_NULL(test, sunxi_rproc_da_to_va(&ctx->rproc, 0x10000000, 0x100, NULL)); + KUNIT_EXPECT_NULL(test, sunxi_rproc_da_to_va(&ctx->rproc, 0x60000000, 0x100, NULL)); +} + +static void test_da_to_va_sram_boundaries(struct kunit *test) +{ + struct test_context *ctx = create_test_ctx(test); + void *va; + + /* 1 byte before Space 0 start -> NULL */ + KUNIT_EXPECT_NULL(test, + sunxi_rproc_da_to_va(&ctx->rproc, + E907_SRAM_SPACE0_DA - 1, 1, NULL)); + + /* Exact last byte inside Space 0 -> valid */ + va = sunxi_rproc_da_to_va(&ctx->rproc, + E907_SRAM_SPACE0_DA_ALT + A527_SRAM_SIZE - 1, 1, NULL); + KUNIT_ASSERT_NOT_NULL(test, va); + + /* 1 byte beyond Space 1 end -> NULL */ + KUNIT_EXPECT_NULL(test, + sunxi_rproc_da_to_va(&ctx->rproc, + E907_SRAM_SPACE1_DA_ALT + A527_SRAM1_SIZE, 1, NULL)); + + /* Access starting inside Space 1 but spanning past end -> NULL */ + KUNIT_EXPECT_NULL(test, + sunxi_rproc_da_to_va(&ctx->rproc, + E907_SRAM_SPACE1_DA_ALT + + A527_SRAM1_SIZE - 4, 8, NULL)); +} + +static void test_da_to_va_space1_boundaries(struct kunit *test) +{ + struct test_context *ctx = create_test_ctx(test); + void *va; + + /* Exact last byte inside Space 1 -> valid */ + va = sunxi_rproc_da_to_va(&ctx->rproc, E907_SRAM_SPACE1_DA + A527_SRAM1_SIZE - 1, 1, NULL); + KUNIT_ASSERT_NOT_NULL(test, va); + + /* Spanning past Space 1 end -> NULL */ + KUNIT_EXPECT_NULL(test, + sunxi_rproc_da_to_va(&ctx->rproc, + E907_SRAM_SPACE1_DA + A527_SRAM1_SIZE - 4, 8, NULL)); +} + +static void test_da_to_va_unmapped_regions_return_null(struct kunit *test) +{ + struct test_context *ctx = create_test_ctx(test); + + /* When SRAM Space 0 is unmapped, all Space 0 views return NULL */ + ctx->priv.r_sram_va = NULL; + KUNIT_EXPECT_NULL(test, + sunxi_rproc_da_to_va(&ctx->rproc, + E907_SRAM_SPACE0_DA_ALT, 0x100, NULL)); + KUNIT_EXPECT_NULL(test, + sunxi_rproc_da_to_va(&ctx->rproc, + A527_SRAM_PHYS, 0x100, NULL)); + KUNIT_EXPECT_NULL(test, + sunxi_rproc_da_to_va(&ctx->rproc, + E907_SRAM_C_DA, 0x100, NULL)); + + /* When SRAM Space 1 is unmapped, Space 1 views return NULL */ + ctx->priv.r_sram1_va = NULL; + KUNIT_EXPECT_NULL(test, + sunxi_rproc_da_to_va(&ctx->rproc, + E907_SRAM_SPACE1_DA, 0x100, NULL)); + KUNIT_EXPECT_NULL(test, + sunxi_rproc_da_to_va(&ctx->rproc, + A527_SRAM1_PHYS, 0x100, NULL)); + + /* When DRAM and Trace are unmapped, they return NULL */ + ctx->priv.dram_va = NULL; + KUNIT_EXPECT_NULL(test, sunxi_rproc_da_to_va(&ctx->rproc, A527_DRAM_PHYS, 0x100, NULL)); + ctx->priv.trace_va = NULL; + KUNIT_EXPECT_NULL(test, sunxi_rproc_da_to_va(&ctx->rproc, A527_TRACE_PHYS, 0x100, NULL)); +} + +static void test_da_to_va_space_isolation(struct kunit *test) +{ + struct test_context *ctx = create_test_ctx(test); + + /* + * Space 1 DA (E907_SRAM_SPACE1_DA) must NEVER resolve to Space 0, even when + * Space 1 is unmapped. + */ + ctx->priv.r_sram1_va = NULL; + KUNIT_EXPECT_NULL(test, + sunxi_rproc_da_to_va(&ctx->rproc, + E907_SRAM_SPACE1_DA, 0x100, NULL)); +} + +static void test_da_to_va_exact_upper_boundary_space0(struct kunit *test) +{ + struct test_context *ctx = create_test_ctx(test); + void *va; + + /* Exact last byte of Space 0 (E907_SRAM_SPACE0_DA + 256K - 1) */ + va = sunxi_rproc_da_to_va(&ctx->rproc, E907_SRAM_SPACE0_DA + A527_SRAM_SIZE - 1, 1, NULL); + KUNIT_ASSERT_NOT_NULL(test, va); + KUNIT_EXPECT_PTR_EQ(test, va, FAKE_SRAM_PTR + A527_SRAM_SIZE - 1); + + /* Spanning 1 byte beyond Space 0 must be rejected */ + KUNIT_EXPECT_NULL(test, + sunxi_rproc_da_to_va(&ctx->rproc, + E907_SRAM_SPACE0_DA + A527_SRAM_SIZE - 1, 2, NULL)); + + /* Exact last byte of Alt Space 0 (E907_SRAM_SPACE0_DA_ALT + 256K - 1) */ + va = sunxi_rproc_da_to_va(&ctx->rproc, + E907_SRAM_SPACE0_DA_ALT + A527_SRAM_SIZE - 1, 1, NULL); + KUNIT_ASSERT_NOT_NULL(test, va); + KUNIT_EXPECT_PTR_EQ(test, va, FAKE_SRAM_PTR + A527_SRAM_SIZE - 1); + + /* Spanning 1 byte beyond Alt Space 0 must be rejected */ + KUNIT_EXPECT_NULL(test, + sunxi_rproc_da_to_va(&ctx->rproc, + E907_SRAM_SPACE0_DA_ALT + + A527_SRAM_SIZE - 1, 2, NULL)); +} + +static void test_da_to_va_exact_upper_boundary_space1(struct kunit *test) +{ + struct test_context *ctx = create_test_ctx(test); + void *va; + + /* Exact last byte of Space 1 (E907_SRAM_SPACE1_DA + 256K - 1) */ + va = sunxi_rproc_da_to_va(&ctx->rproc, E907_SRAM_SPACE1_DA + A527_SRAM1_SIZE - 1, 1, NULL); + KUNIT_ASSERT_NOT_NULL(test, va); + KUNIT_EXPECT_PTR_EQ(test, va, FAKE_SRAM1_PTR + A527_SRAM1_SIZE - 1); + + /* Spanning 1 byte beyond Space 1 must be rejected */ + KUNIT_EXPECT_NULL(test, + sunxi_rproc_da_to_va(&ctx->rproc, + E907_SRAM_SPACE1_DA + A527_SRAM1_SIZE - 1, 2, NULL)); +} + +static void test_da_to_va_exact_upper_boundary_dram(struct kunit *test) +{ + struct test_context *ctx = create_test_ctx(test); + void *va; + + /* Exact last byte of DRAM carveout */ + va = sunxi_rproc_da_to_va(&ctx->rproc, A527_DRAM_PHYS + A527_DRAM_SIZE - 1, 1, NULL); + KUNIT_ASSERT_NOT_NULL(test, va); + KUNIT_EXPECT_PTR_EQ(test, va, FAKE_DRAM_VA + A527_DRAM_SIZE - 1); + + /* Spanning 1 byte beyond DRAM carveout must be rejected */ + KUNIT_EXPECT_NULL(test, + sunxi_rproc_da_to_va(&ctx->rproc, + A527_DRAM_PHYS + A527_DRAM_SIZE - 1, 2, NULL)); +} + +static void test_da_to_va_a733_sram_a2_layout(struct kunit *test) +{ + struct test_context *ctx = create_test_ctx(test); + void *va; + bool is_iomem = false; + + /* + * Allwinner A733 (sun60i) E902 silicon profile: + * Uses System SRAM A2 (0x00040000 - 0x00073FFF, 208 KB). + * Has no Space 1 (r_sram1_va is NULL). + */ + ctx->priv.r_sram_phys = 0x00040000ULL; + ctx->priv.r_sram_size = 0x34000; /* 208 KB */ + ctx->priv.r_sram1_va = NULL; + ctx->priv.r_sram1_size = 0; + + /* Base of SRAM A2 */ + va = sunxi_rproc_da_to_va(&ctx->rproc, 0x00040000, 0x100, &is_iomem); + KUNIT_ASSERT_NOT_NULL(test, va); + KUNIT_EXPECT_PTR_EQ(test, va, FAKE_SRAM_PTR); + KUNIT_EXPECT_TRUE(test, is_iomem); + + /* Entry offset in SRAM A2 (0x00044000) */ + va = sunxi_rproc_da_to_va(&ctx->rproc, 0x00044000, 0x1000, &is_iomem); + KUNIT_ASSERT_NOT_NULL(test, va); + KUNIT_EXPECT_PTR_EQ(test, va, FAKE_SRAM_PTR + 0x4000); + + /* Exact last byte of SRAM A2 (0x00040000 + 0x34000 - 1 = 0x00073FFF) */ + va = sunxi_rproc_da_to_va(&ctx->rproc, 0x00073FFF, 1, &is_iomem); + KUNIT_ASSERT_NOT_NULL(test, va); + KUNIT_EXPECT_PTR_EQ(test, va, FAKE_SRAM_PTR + 0x33FFF); + + /* Beyond SRAM A2 boundary must return NULL */ + KUNIT_EXPECT_NULL(test, + sunxi_rproc_da_to_va(&ctx->rproc, 0x00074000, 1, NULL)); + KUNIT_EXPECT_NULL(test, + sunxi_rproc_da_to_va(&ctx->rproc, 0x00073FFF, 2, NULL)); +} + +static void test_da_to_va_unaligned_lengths(struct kunit *test) +{ + struct test_context *ctx = create_test_ctx(test); + void *va; + + /* 3-byte and 7-byte transfers must translate correctly without faulting */ + va = sunxi_rproc_da_to_va(&ctx->rproc, E907_SRAM_SPACE0_DA + 3, 3, NULL); + KUNIT_ASSERT_NOT_NULL(test, va); + KUNIT_EXPECT_PTR_EQ(test, va, FAKE_SRAM_PTR + 3); + + va = sunxi_rproc_da_to_va(&ctx->rproc, E907_SRAM_SPACE1_DA + 7, 7, NULL); + KUNIT_ASSERT_NOT_NULL(test, va); + KUNIT_EXPECT_PTR_EQ(test, va, FAKE_SRAM1_PTR + 7); +} + +static void test_da_to_va_malformed_rsc_table_entry(struct kunit *test) +{ + struct test_context *ctx = create_test_ctx(test); + + /* + * Malformed resource table entry: firmware requests a VirtIO vring + * or carveout pointing to an invalid device address (e.g. 0xDEADBEEF). + * da_to_va must return NULL, prompting rproc_elf_load_rsc_table to fail + * safely rather than writing into unmapped space. + */ + KUNIT_EXPECT_NULL(test, sunxi_rproc_da_to_va(&ctx->rproc, 0xDEADBEEF, 0x1000, NULL)); + + /* Out-of-window unmapped device address (0x30000000) */ + KUNIT_EXPECT_NULL(test, sunxi_rproc_da_to_va(&ctx->rproc, 0x30000000, 0x1000, NULL)); +} + +static void test_da_to_va_corrupted_elf_overflow_segment(struct kunit *test) +{ + struct test_context *ctx = create_test_ctx(test); + + /* + * Corrupted ELF header: segment has an excessive memsz that wraps around + * 64-bit integer limits or spans across window bounds. + */ + KUNIT_EXPECT_NULL(test, + sunxi_rproc_da_to_va(&ctx->rproc, + E907_SRAM_SPACE0_DA_ALT, + (size_t)-1, NULL)); + KUNIT_EXPECT_NULL(test, + sunxi_rproc_da_to_va(&ctx->rproc, + E907_SRAM_SPACE1_DA, + (size_t)-16, NULL)); + + /* Segment starts near end of Space 0 and extends 4KB beyond valid SRAM */ + KUNIT_EXPECT_NULL(test, + sunxi_rproc_da_to_va(&ctx->rproc, + E907_SRAM_SPACE0_DA_ALT + A527_SRAM_SIZE - 0x100, + 0x200, NULL)); +} + +static void test_start_a733_mode1_and_mode2_bootaddr(struct kunit *test) +{ + struct test_context *ctx = create_test_ctx(test); + int ret; + + ctx->priv.cfg_va = (void __iomem *)ctx->mock_cfg_regs; + + /* Mode 1: Suspend/resume E902 SCP boot from DRAM (0x40014000) */ + ctx->rproc.bootaddr = 0x40014000; + ret = sunxi_rproc_start(&ctx->rproc); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, ctx->mock_cfg_regs[E906_STA_ADD_REG / 4], 0x40014000U); + + /* Mode 2: Real-time coprocessor boot from SRAM A2 (0x00044000) */ + ctx->rproc.bootaddr = 0x00044000; + ret = sunxi_rproc_start(&ctx->rproc); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, ctx->mock_cfg_regs[E906_STA_ADD_REG / 4], 0x00044000U); +} + +/* ==================== Lifecycle: start & stop ==================== */ + +static void test_start_bootaddr_programming(struct kunit *test) +{ + struct test_context *ctx = create_test_ctx(test); + int ret; + + ctx->priv.cfg_va = (void __iomem *)ctx->mock_cfg_regs; + ctx->rproc.bootaddr = 0x40014000; + + ret = sunxi_rproc_start(&ctx->rproc); + KUNIT_EXPECT_EQ(test, ret, 0); + + /* Check that bootaddr was written to STA_ADD_REG (offset 0x204) */ + KUNIT_EXPECT_EQ(test, ctx->mock_cfg_regs[E906_STA_ADD_REG / 4], 0x40014000U); +} + +static void test_start_rejects_bootaddr_overflow(struct kunit *test) +{ + struct test_context *ctx = create_test_ctx(test); + int ret; + + /* Addresses beyond 32-bit range are invalid for 32-bit XuanTie E907 */ + ctx->rproc.bootaddr = 0x100000000ULL; + + ret = sunxi_rproc_start(&ctx->rproc); + KUNIT_EXPECT_EQ(test, ret, -EINVAL); +} + +static void test_stop_succeeds(struct kunit *test) +{ + struct test_context *ctx = create_test_ctx(test); + int ret; + + ret = sunxi_rproc_stop(&ctx->rproc); + KUNIT_EXPECT_EQ(test, ret, 0); +} + +/* ==================== Lifecycle: prepare & unprepare ==================== */ + +static void test_prepare_and_unprepare_remap(struct kunit *test) +{ + struct test_context *ctx = create_test_ctx(test); + int ret; + + ctx->priv.remap_va = (void __iomem *)&ctx->mock_remap_reg; + ctx->mock_remap_reg = 0; + + /* Provide real buffers for SRAM clearing to prevent faulting on fake VA */ + ctx->priv.r_sram_va = (void __iomem *)ctx->mock_sram_buf; + ctx->priv.r_sram_size = sizeof(ctx->mock_sram_buf); + ctx->priv.r_sram1_va = (void __iomem *)ctx->mock_sram1_buf; + ctx->priv.r_sram1_size = sizeof(ctx->mock_sram1_buf); + + /* prepare() should set SUNXI_REMAP_SRAMA3_2_BIT (bit 1) */ + ret = sunxi_rproc_prepare(&ctx->rproc); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, ctx->mock_remap_reg & SUNXI_REMAP_SRAMA3_2_BIT, + SUNXI_REMAP_SRAMA3_2_BIT); + + /* unprepare() should clear SUNXI_REMAP_SRAMA3_2_BIT */ + ret = sunxi_rproc_unprepare(&ctx->rproc); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, ctx->mock_remap_reg & SUNXI_REMAP_SRAMA3_2_BIT, 0U); +} + +static void test_prepare_clears_sram(struct kunit *test) +{ + struct test_context *ctx = create_test_ctx(test); + int i, ret; + + memset(ctx->mock_sram_buf, 0xAA, sizeof(ctx->mock_sram_buf)); + memset(ctx->mock_sram1_buf, 0x55, sizeof(ctx->mock_sram1_buf)); + + ctx->priv.r_sram_va = (void __iomem *)ctx->mock_sram_buf; + ctx->priv.r_sram_size = sizeof(ctx->mock_sram_buf); + + ctx->priv.r_sram1_va = (void __iomem *)ctx->mock_sram1_buf; + ctx->priv.r_sram1_size = sizeof(ctx->mock_sram1_buf); + + ret = sunxi_rproc_prepare(&ctx->rproc); + KUNIT_EXPECT_EQ(test, ret, 0); + + /* Both SRAM buffers must be cleanly zeroed to prevent stale data / ECC faults */ + for (i = 0; i < sizeof(ctx->mock_sram_buf); i++) + KUNIT_EXPECT_EQ(test, ctx->mock_sram_buf[i], 0); + + for (i = 0; i < sizeof(ctx->mock_sram1_buf); i++) + KUNIT_EXPECT_EQ(test, ctx->mock_sram1_buf[i], 0); +} + +/* ==================== Operations: kick ==================== */ + +static void test_kick_null_tx_chan_safe(struct kunit *test) +{ + struct test_context *ctx = create_test_ctx(test); + + ctx->priv.tx_chan = NULL; + /* Must return cleanly without NULL dereference */ + sunxi_rproc_kick(&ctx->rproc, 0); + sunxi_rproc_kick(&ctx->rproc, 1); +} + +static void test_kick_stores_vqid(struct kunit *test) +{ + struct test_context *ctx = create_test_ctx(test); + + /* Verify kick_msg stores the passed vqid to avoid stack UAF */ + ctx->priv.kick_msg = 0xDEADBEEF; + ctx->priv.tx_chan = NULL; /* Avoid mbox_send_message dispatch */ + + sunxi_rproc_kick(&ctx->rproc, 1); + /* Without tx_chan, returns before writing kick_msg */ + KUNIT_EXPECT_EQ(test, ctx->priv.kick_msg, 0xDEADBEEFU); +} + +/* ==================== Test Suite Registration ==================== */ + +static struct kunit_case sunxi_rproc_test_cases[] = { + /* da_to_va Guard Tests */ + KUNIT_CASE(test_da_to_va_zero_length_returns_null), + KUNIT_CASE(test_da_to_va_overflow_guard), + KUNIT_CASE(test_da_to_va_null_is_iomem_safe), + /* da_to_va Space 0 Tests */ + KUNIT_CASE(test_da_to_va_sram_space0_core_da), + KUNIT_CASE(test_da_to_va_sram_space0_core_da_offset), + KUNIT_CASE(test_da_to_va_sram_space0_host_phys), + KUNIT_CASE(test_da_to_va_sram_space0_host_phys_offset), + KUNIT_CASE(test_da_to_va_sram_space0_alt_3ff80000), + KUNIT_CASE(test_da_to_va_sram_space0_pubsram_c_da), + /* da_to_va Space 1 Tests */ + KUNIT_CASE(test_da_to_va_sram_space1_host_phys), + KUNIT_CASE(test_da_to_va_sram_space1_40000000), + KUNIT_CASE(test_da_to_va_sram_space1_40040000), + /* da_to_va DRAM & Trace Tests */ + KUNIT_CASE(test_da_to_va_dram_direct), + KUNIT_CASE(test_da_to_va_trace_direct), + /* da_to_va Out of Range, Boundary, Unmapped & Isolation Tests */ + KUNIT_CASE(test_da_to_va_out_of_range), + KUNIT_CASE(test_da_to_va_sram_boundaries), + KUNIT_CASE(test_da_to_va_space1_boundaries), + KUNIT_CASE(test_da_to_va_unmapped_regions_return_null), + KUNIT_CASE(test_da_to_va_space_isolation), + KUNIT_CASE(test_da_to_va_exact_upper_boundary_space0), + KUNIT_CASE(test_da_to_va_exact_upper_boundary_space1), + KUNIT_CASE(test_da_to_va_exact_upper_boundary_dram), + KUNIT_CASE(test_da_to_va_a733_sram_a2_layout), + KUNIT_CASE(test_da_to_va_unaligned_lengths), + KUNIT_CASE(test_da_to_va_malformed_rsc_table_entry), + KUNIT_CASE(test_da_to_va_corrupted_elf_overflow_segment), + /* Lifecycle: start and stop */ + KUNIT_CASE(test_start_bootaddr_programming), + KUNIT_CASE(test_start_a733_mode1_and_mode2_bootaddr), + KUNIT_CASE(test_start_rejects_bootaddr_overflow), + KUNIT_CASE(test_stop_succeeds), + /* Lifecycle: prepare and unprepare */ + KUNIT_CASE(test_prepare_and_unprepare_remap), + KUNIT_CASE(test_prepare_clears_sram), + /* Operations: kick */ + KUNIT_CASE(test_kick_null_tx_chan_safe), + KUNIT_CASE(test_kick_stores_vqid), + {} +}; + +static struct kunit_suite sunxi_rproc_test_suite = { + .name = "sunxi_rproc", + .test_cases = sunxi_rproc_test_cases, +}; + +kunit_test_suite(sunxi_rproc_test_suite); + +MODULE_AUTHOR("Tim Michals "); +MODULE_DESCRIPTION("Comprehensive KUnit tests for Allwinner sunxi remoteproc driver"); +MODULE_LICENSE("GPL"); From patchwork Sun Sep 27 00:20:16 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tim Michals X-Patchwork-Id: 3495 Return-Path: X-Original-To: noreply@patchwork.local Delivered-To: noreply@patchwork.local Received: from sea.lore.kernel.org (sea.lore.kernel.org [172.234.253.10]) by mxe881.netcup.net (Postfix) with ESMTPS id CB1FF1C1033 for ; Sun, 27 Sep 2026 02:23:10 +0200 (CEST) Authentication-Results: mxe881; dkim=pass header.d=gmail.com; spf=pass (sender IP is 172.234.253.10) smtp.mailfrom=linux-sunxi+bounces-26297-noreply=patchwork.local@lists.linux.dev smtp.helo=sea.lore.kernel.org Received-SPF: pass (mxe881: domain of lists.linux.dev designates 172.234.253.10 as permitted sender) client-ip=172.234.253.10; envelope-from=linux-sunxi+bounces-26297-noreply=patchwork.local@lists.linux.dev; helo=sea.lore.kernel.org; Received: from smtp.subspace.kernel.org (conduit.subspace.kernel.org [100.90.174.1]) by sea.lore.kernel.org (Postfix) with ESMTP id 63D9E3535B for ; Sun, 27 Sep 2026 00:21:08 +0000 (UTC) Received: from localhost.localdomain (localhost.localdomain [127.0.0.1]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 3B7D524E4C6; Sun, 27 Sep 2026 00:21:08 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="sIUmwKXX" X-Original-To: linux-sunxi@lists.linux.dev Received: from mail-oo2-f38.google.com (mail-oo2-f38.google.com [74.125.231.166]) (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 BE46F257ACF for ; Sun, 27 Sep 2026 00:21:05 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.231.166 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790468468; cv=none; b=W/67FXYySzw2Aiy6LtXEaO7J2Mg/wCBlAlC8yPR5K0ZASsCnsGFmyOEbMxiTgmx+lpntuwCMv33YC0gujzdynR237S/TeyOciGbEGgMRUZtyrjPV89qziMJ2qezvt+4EhNXw33kECTWJkc234gTV46u9fK14WSrvMyAuxlNKdQU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790468468; c=relaxed/simple; bh=8/pV73x78jrNfWMKzRpgQhL/uS+KEamSLh1zpjcATE4=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=DnHOrG8djOQWrgZT8efwo05wLxE48p1GuZcgJiYpNlOo+/wdkqgox99g6siJiJ0a/Sj1L31dH/zQCmNbpniiJNBYWDKGWM36oJVFrvz8x49h5/B6M3WcUJKp3WHEReIPmOfMHZBw3W0aRRPljjj2nuqnhqO8DG6JqjcIcCP8duw= 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=sIUmwKXX; arc=none smtp.client-ip=74.125.231.166 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 Received: by mail-oo2-f38.google.com with SMTP id 46e09a7af769-81be5042c56so50846a34.0 for ; Sat, 26 Sep 2026 17:21:05 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1790468464; x=1791073264; darn=lists.linux.dev; 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=TIkNGJES0hm9OJqnA5eP6Pl3OVYCfTyaqkdgFUAgvWM=; b=sIUmwKXXGQNxUq7+tYNiyZdO3PBJmeyqb6xDrOEsOPEXf1Jp/Of/YjY95Cdo9sE0s3 049DECAQSc5ffFy1EeWIjFO6Hrb2Af2ueeiWBLUE9KPJdayEhFW1rQ0NVRS4cHch5XGJ prZl9wBaB4CgExDr+fuyzGW61F0C1Sid9oYvCOizh0GE8XXhEEfquOJT+OA4uSlEgra9 q1nhlPuMgjkbT+Tw6quJdj38XOghqNHNZoVwmcnkeAONuu/QrSzCdJYvk5Vq4XzL86Au HQbeRKRUd8Y+viZ2qmNMXpymKbVY/jGMvWlNgzo8C2Co1Mu8csG8v5XMKpYoc7JkPHYq Es4A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1790468464; x=1791073264; 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=TIkNGJES0hm9OJqnA5eP6Pl3OVYCfTyaqkdgFUAgvWM=; b=uITsG440G1b1v6xBDpKTcoHhpueLochTBABJbaKixf8O8YoSS7CjBADqiQmnLxLFlh DAvCoWMhk9q/bsT3M3lwhxeLzl4c6F94UbggfzmBsVTikZH9BNlfTXaVdf3EFnz/yTsW 2Jy2EDyYyHDhy2tuStAxShselNZdNH60qSgnURsQBzzErotJaGXsT++8mnpMiQIIr+ul +YQ98sh78yQfwDsEIPmpb4I6sknjNs7npUCmcD9lAHxH4SE60rn0kGiAUY5paip2vcqR 64+7FnsGT235s6/9JbLh7Wqb5RuLnOlrj7f9DCEtAn86LBaEY4zjCmAiRVDU5fM7oHW6 Vxmw== X-Gm-Message-State: AFuF++ktDrOYqSRCeXixfGRXHQL5n4Z+nBNWaDldG2c/Wgidw/MvFvvJ Xndkcr66qyrJu4H4dFHziKnk8u4pVehecf2xwWS4vIXl3s+wpLwTOhIGNzhQ+FRM X-Gm-Gg: AYBFou10xDUm1ecpPbRQYoYSNnDdYswENMFlqVR7BhDj99RZr++wj06o7CRn55g57cC CTaZCH0+Gt6m6PTKJ2pcCL9UwbdESD/iZIJdNyFD0pz/yHvveEEea0RmSGwZAKq/QcCViLr8Zmh lcn+EWAUYGFHlYjVqWbnWPYdTm2o2HQ2UbxLEBAyt4YIlgFw/pZqhjZswIw8nEUGFUAnEWzrJy3 +R9q2hv4LAfSoy76PlEr9zy2PfMhNCwhvxj0APvYe1aWpwV4LmO+cFDEMFCUYLxuA21+X+ZgIZ8 QLc220J06xHUo/7Lfr3s1dz020uQI6y9vfZZjCDb4KnTDDE89WQ7fXwZhW2KQfZMKZAW1/AHcHu QlgJYH6e8VF40kGVwkbHOuta7Wofp4fNoX+cVbkodv6Tcw3SlnLr1aEADzgrfK21tridZKuJ9Dl qFuYYgjMY7RsrgOJBRKQjmfoQ2hz8P/emTZ35AuuvBA0rZEM8P+5mPPET3lxogIwdmTKq3dsasu I81miVjlSBrA3XEt8lBQQ== X-Received: by 2002:a05:6820:61d:b0:6be:137b:3f98 with SMTP id 006d021491bc7-6d5b869c1f6mr6321898eaf.22.1790468464256; Sat, 26 Sep 2026 17:21:04 -0700 (PDT) Received: from tcmichals-5759.lan ([2607:fb91:3584:35a9:66f8:2cb5:aa80:21fd]) by smtp.gmail.com with ESMTPSA id 006d021491bc7-6d8828e1ea0sm838989eaf.6.2026.09.26.17.21.02 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 26 Sep 2026 17:21:03 -0700 (PDT) From: Tim Michals To: linux-sunxi@lists.linux.dev, linux-remoteproc@vger.kernel.org Cc: devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, wens@kernel.org, jernej.skrabec@gmail.com, samuel@sholland.org, andersson@kernel.org, mathieu.poirier@linaro.org, jassisinghbrar@gmail.com, robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org, Tim Michals Subject: [PATCH v2 7/7] arm64: dts: allwinner: add a523 msgbox and remoteproc nodes Date: Sat, 26 Sep 2026 19:20:16 -0500 Message-ID: <20260927002021.797069-8-tcmichals@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260927002021.797069-1-tcmichals@gmail.com> References: <20260922034711.190253-1-tcmichals@gmail.com> <20260927002021.797069-1-tcmichals@gmail.com> Precedence: bulk X-Mailing-List: linux-sunxi@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Rspamd-Server: rspamd-worker-8404 X-Spamd-Result: default: False [0.34 / 15.00]; BAYES_HAM(-5.50)[100.00%]; RBL_SENDERSCORE(2.00)[172.234.253.10:from]; SUSPICIOUS_RECIPS(1.50)[]; MID_CONTAINS_FROM(1.00)[]; DMARC_POLICY_SOFTFAIL(1.00)[gmail.com : SPF not aligned (relaxed), No valid DKIM,none]; R_MISSING_CHARSET(0.50)[]; MAILLIST(-0.15)[generic]; BAD_REP_POLICIES(0.10)[]; MIME_GOOD(-0.10)[text/plain]; HAS_LIST_UNSUB(-0.01)[]; FREEMAIL_CC(0.00)[vger.kernel.org,lists.infradead.org,kernel.org,gmail.com,sholland.org,linaro.org]; DBL_PROHIBIT(0.00)[0.45.222.48:email]; RCPT_COUNT_TWELVE(0.00)[15]; DBL_BLOCKED_OPENRESOLVER(0.00)[sea.lore.kernel.org:rdns,sea.lore.kernel.org:helo,0.45.206.144:email,0.108.203.144:email,0.45.210.120:email,0.108.172.80:email]; FORGED_SENDER_MAILLIST(0.00)[]; TO_DN_SOME(0.00)[]; R_SPF_ALLOW(0.00)[+ip4:172.234.253.10:c]; TAGGED_RCPT(0.00)[dt]; FROM_HAS_DN(0.00)[]; RCVD_TLS_LAST(0.00)[]; ASN(0.00)[asn:63949, ipnet:172.234.224.0/19, country:SG]; MIME_TRACE(0.00)[0:+]; FORGED_RECIPIENTS_MAILLIST(0.00)[]; PRECEDENCE_BULK(0.00)[]; TAGGED_FROM(0.00)[bounces-26297-noreply=patchwork.local]; FROM_NEQ_ENVFROM(0.00)[tcmichals@gmail.com,linux-sunxi@lists.linux.dev]; ARC_ALLOW(0.00)[subspace.kernel.org:s=arc-20240116:i=1]; FREEMAIL_FROM(0.00)[gmail.com]; RCVD_COUNT_FIVE(0.00)[6]; RCVD_VIA_SMTP_AUTH(0.00)[] X-Rspamd-Queue-Id: CB1FF1C1033 X-MORS-Enabled: yes X-MORS-DOMAIN: patchwork.local X-MORS-HOSTING: hosting172546 X-MORS-USER: hosting172546 X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= Add the hardware mailbox (CPUX_MSGBOX) node and the XuanTie E907 RISC-V remoteproc node for Allwinner A523/A527/T527 SoCs. Connect the remoteproc node to the MCU CCU clocks (bus, core, sram, msgbox) and resets (cfg, core, sram, msgbox) and mailbox channels 8 & 9 for bidirectional host-coprocessor IPC. Signed-off-by: Tim Michals --- .../arm64/boot/dts/allwinner/sun55i-a523.dtsi | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/arch/arm64/boot/dts/allwinner/sun55i-a523.dtsi b/arch/arm64/boot/dts/allwinner/sun55i-a523.dtsi index 85ef492ffeae..b4699c4df5e9 100644 --- a/arch/arm64/boot/dts/allwinner/sun55i-a523.dtsi +++ b/arch/arm64/boot/dts/allwinner/sun55i-a523.dtsi @@ -605,6 +605,23 @@ dma: dma-controller@3002000 { #dma-cells = <1>; }; + msgbox: mailbox@3003000 { + compatible = "allwinner,sun55i-a523-msgbox"; + reg = <0x03003000 0x1000>, + <0x07094000 0x1000>, + <0x07120000 0x1000>, + <0x07136000 0x1000>; + reg-names = "arm", "dsp", "cpus", "rv"; + clocks = <&ccu CLK_BUS_MSGBOX>; + resets = <&ccu RST_BUS_MSGBOX>; + interrupts = , + , + , + ; + interrupt-names = "arm", "dsp", "cpus", "rv"; + #mbox-cells = <1>; + }; + sid: efuse@3006000 { compatible = "allwinner,sun55i-a523-sid", "allwinner,sun50i-a64-sid"; @@ -1144,5 +1161,27 @@ npu: npu@7122000 { resets = <&mcu_ccu RST_BUS_MCU_NPU>; power-domains = <&ppu PD_NPU>; }; + + rproc: remoteproc@7130000 { + compatible = "allwinner,sun55i-a523-rproc"; + reg = <0x07130000 0x1000>, + <0x07280000 0x40000>, + <0x072c0000 0x40000>, + <0x07010364 0x4>; + reg-names = "cfg", "r_sram", "r_sram1", "remap"; + clocks = <&mcu_ccu CLK_BUS_MCU_RISCV_CFG>, + <&mcu_ccu CLK_MCU_RISCV>, + <&mcu_ccu CLK_BUS_MCU_PUBSRAM>, + <&mcu_ccu CLK_BUS_MCU_RISCV_MSGBOX>; + clock-names = "bus", "core", "sram", "msgbox"; + resets = <&mcu_ccu RST_BUS_MCU_RISCV_CFG>, + <&mcu_ccu RST_BUS_MCU_RISCV_CORE>, + <&mcu_ccu RST_BUS_MCU_PUBSRAM>, + <&mcu_ccu RST_BUS_MCU_RISCV_MSGBOX>; + reset-names = "cfg", "core", "sram", "msgbox"; + mboxes = <&msgbox 8>, <&msgbox 9>; + mbox-names = "rx", "tx"; + status = "disabled"; + }; }; };