From patchwork Mon Aug 19 16:02:22 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ian Brockbank X-Patchwork-Id: 13768580 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id B5A2FC3DA4A for ; Mon, 19 Aug 2024 16:12:41 +0000 (UTC) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1sg4zU-00033Z-Nk; Mon, 19 Aug 2024 12:12:20 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1sg4zT-0002xP-9V; Mon, 19 Aug 2024 12:12:19 -0400 Received: from mx0b-001ae601.pphosted.com ([67.231.152.168]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1sg4zO-0007Z6-DK; Mon, 19 Aug 2024 12:12:19 -0400 Received: from pps.filterd (m0077474.ppops.net [127.0.0.1]) by mx0b-001ae601.pphosted.com (8.18.1.2/8.18.1.2) with ESMTP id 47JF6kmr012798; Mon, 19 Aug 2024 11:11:57 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=cirrus.com; h=cc :content-transfer-encoding:content-type:date:from:in-reply-to :message-id:mime-version:references:subject:to; s= PODMain02222019; bh=j7hGApsJVqvt0vzVQMiwFY612vDcBZvRRAiTdsp0Aq0=; b= Aq9RI8FiqZiNtWYnFMFF5o2ZgXxyeaXAlRZjfHEnoDY7yZAOyn+++jFDkM6Ez+Yx DfJi7F73KKhWhbyp2EOLaksb0VupupgYWuAAOGszjYdZDCZgASuAtzVTLfXCPSku lv0GXdXV5xTpnN0rjBlrlbTzIHeRskX3fDL7ItBvdYI70FpsDFgwRWsDud001+dI 0f9HH340hM1G2CEuos4MesYwfmY3kOLk/uOYorWdYXfhu1v+arY+/TsBqFa+KoqK DJYai/oOUC/nXx0iIFda2IBsBknS2H1TAG+26b2A/V5AsUnmvUFwI5/lte/CmGNM alkd69zzZOsZbcVW/ly8Fw== Received: from ausex02.ad.cirrus.com ([141.131.3.21]) by mx0b-001ae601.pphosted.com (PPS) with ESMTPS id 412r9hsxbq-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Mon, 19 Aug 2024 11:11:56 -0500 (CDT) Received: from ausex01.ad.cirrus.com (141.131.37.95) by ausex02.ad.cirrus.com (141.131.37.96) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1544.9; Mon, 19 Aug 2024 11:11:56 -0500 Received: from EDIN7BQBTG3.ad.cirrus.com (141.131.38.212) by anon-ausex01.ad.cirrus.com (141.131.37.95) with Microsoft SMTP Server id 15.2.1544.9 via Frontend Transport; Mon, 19 Aug 2024 11:11:48 -0500 From: Ian Brockbank To: , CC: Palmer Dabbelt , Alistair Francis , Bin Meng , Weiwei Li , Daniel Henrique Barboza , Liu Zhiwei , Ian Brockbank , Ian Brockbank , Troy Song Subject: [PATCH 11/11 v2] tests: add riscv clic qtest case and a function in qtest Date: Mon, 19 Aug 2024 17:02:22 +0100 Message-ID: <20240819160742.27586-15-Ian.Brockbank@cirrus.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20240819160742.27586-1-Ian.Brockbank@cirrus.com> References: <20240819160742.27586-1-Ian.Brockbank@cirrus.com> MIME-Version: 1.0 X-Proofpoint-GUID: wHjusZ7qPR85PS3mZhD8ZOwGepVPfC6d X-Proofpoint-ORIG-GUID: wHjusZ7qPR85PS3mZhD8ZOwGepVPfC6d X-Proofpoint-Spam-Reason: safe Received-SPF: pass client-ip=67.231.152.168; envelope-from=prvs=5961d01319=ian.brockbank@cirrus.com; helo=mx0b-001ae601.pphosted.com X-Spam_score_int: -27 X-Spam_score: -2.8 X-Spam_bar: -- X-Spam_report: (-2.8 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_LOW=-0.7, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, T_SCC_BODY_TEXT_LINE=-0.01 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Sender: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org This adds riscv32-clic-test.c, containing qtest test cases for configuring CLIC (via virt machine) and for triggering interrupts. In order to detect the interrupts, qtest.c has been updated to send interrupt information back to the test about the IRQ being delivered. Since we need to both trigger and detect the interrupt, qtest has also been updated to allow both an input and an output GPIO to be intercepted. Signed-off-by: Troy Song Signed-off-by: Ian Brockbank --- hw/intc/riscv_clic.c | 4 + include/sysemu/qtest.h | 2 + system/qtest.c | 72 +- tests/qtest/libqtest.c | 9 + tests/qtest/libqtest.h | 9 + tests/qtest/meson.build | 3 +- tests/qtest/riscv32-clic-test.c | 1928 +++++++++++++++++++++++++++++++ 7 files changed, 2010 insertions(+), 17 deletions(-) create mode 100644 tests/qtest/riscv32-clic-test.c -- 2.46.0.windows.1 This message and any attachments may contain privileged and confidential information that is intended solely for the person(s) to whom it is addressed. If you are not an intended recipient you must not: read; copy; distribute; discuss; take any action in or make any reliance upon the contents of this message; nor open or read any attachment. If you have received this message in error, please notify us as soon as possible on the following telephone number and destroy this message including any attachments. Thank you. Cirrus Logic International (UK) Ltd and Cirrus Logic International Semiconductor Ltd are companies registered in Scotland, with registered numbers SC089839 and SC495735 respectively. Our registered office is at 7B Nightingale Way, Quartermile, Edinburgh, EH3 9EG, UK. Tel: +44 (0)131 272 7000. www.cirrus.com diff --git a/hw/intc/riscv_clic.c b/hw/intc/riscv_clic.c index 1800e84dfd..155ba65492 100644 --- a/hw/intc/riscv_clic.c +++ b/hw/intc/riscv_clic.c @@ -174,6 +174,10 @@ static void riscv_clic_next_interrupt(void *opaque) clic->clicintip[active->irq] = 0; } /* Post pending interrupt for this hart */ + if (qtest_enabled()) { + qemu_set_irq(clic->cpu_irq, qtest_encode_irq(active->irq, 1)); + return; + } clic->exccode = active->irq | mode << RISCV_EXCP_CLIC_MODE_SHIFT | level << RISCV_EXCP_CLIC_LEVEL_SHIFT; diff --git a/include/sysemu/qtest.h b/include/sysemu/qtest.h index c161d75165..1a34d27c6d 100644 --- a/include/sysemu/qtest.h +++ b/include/sysemu/qtest.h @@ -34,6 +34,8 @@ void qtest_server_init(const char *qtest_chrdev, const char *qtest_log, Error ** void qtest_server_set_send_handler(void (*send)(void *, const char *), void *opaque); void qtest_server_inproc_recv(void *opaque, const char *buf); + +int qtest_encode_irq(int irqn, int level); #endif #endif diff --git a/system/qtest.c b/system/qtest.c index 12703a2045..0ba6e0fcbe 100644 --- a/system/qtest.c +++ b/system/qtest.c @@ -49,7 +49,8 @@ struct QTest { bool qtest_allowed; -static DeviceState *irq_intercept_dev; +static DeviceState *irq_intercept_dev_in; +static DeviceState *irq_intercept_dev_out; static FILE *qtest_log_fp; static QTest *qtest; static GString *inbuf; @@ -61,6 +62,14 @@ static void *qtest_server_send_opaque; #define FMT_timeval "%.06f" +/* + * Encoding for passing the specific IRQ information from an interrupt handler + * to QTest. This needs to support CLIC, which has a 12-bit interrupt number. + */ +#define QTEST_IRQN 0x0fff +#define QTEST_IRQN_SHIFT 0 +#define QTEST_IRQ_LEVEL_SHIFT 12 + /** * DOC: QTest Protocol * @@ -311,6 +320,18 @@ void qtest_sendf(CharBackend *chr, const char *fmt, ...) va_end(ap); } +/* Encode the IRQ number and level for QTest */ +int qtest_encode_irq(int irqn, int level) +{ + return (irqn & QTEST_IRQN) | (level << QTEST_IRQ_LEVEL_SHIFT); +} + +static void qtest_decode_irq(int value, int *irqn, int *level) +{ + *irqn = value & QTEST_IRQN; + *level = value >> QTEST_IRQ_LEVEL_SHIFT; +} + static void qtest_irq_handler(void *opaque, int n, int level) { qemu_irq old_irq = *(qemu_irq *)opaque; @@ -320,6 +341,16 @@ static void qtest_irq_handler(void *opaque, int n, int level) CharBackend *chr = &qtest->qtest_chr; irq_levels[n] = level; qtest_send_prefix(chr); + if (level > 1) { + int delivered_irq_num, pin_level; + qtest_decode_irq(level, &delivered_irq_num, &pin_level); + qtest_sendf(chr, "IRQ %s %d\n", + "delivered", delivered_irq_num); + qtest_send_prefix(chr); + qtest_sendf(chr, "IRQ %s %d\n", + pin_level ? "raise" : "lower", n); + return; + } qtest_sendf(chr, "IRQ %s %d\n", level ? "raise" : "lower", n); } @@ -369,6 +400,7 @@ static void qtest_process_command(CharBackend *chr, gchar **words) bool is_named; bool is_outbound; bool interception_succeeded = false; + bool interception_duplicated = false; g_assert(words[1]); is_named = words[2] != NULL; @@ -386,38 +418,46 @@ static void qtest_process_command(CharBackend *chr, gchar **words) return; } - if (irq_intercept_dev) { - qtest_send_prefix(chr); - if (irq_intercept_dev != dev) { - qtest_send(chr, "FAIL IRQ intercept already enabled\n"); - } else { - qtest_send(chr, "OK\n"); - } - return; - } - QLIST_FOREACH(ngl, &dev->gpios, node) { /* We don't support inbound interception of named GPIOs yet */ if (is_outbound) { + if (irq_intercept_dev_out) { + if (irq_intercept_dev_out == dev) { + interception_succeeded = true; + } else { + interception_duplicated = true; + } + } /* NULL is valid and matchable, for "unnamed GPIO" */ - if (g_strcmp0(ngl->name, words[2]) == 0) { + else if (g_strcmp0(ngl->name, words[2]) == 0) { int i; for (i = 0; i < ngl->num_out; ++i) { qtest_install_gpio_out_intercept(dev, ngl->name, i); } + irq_intercept_dev_out = dev; interception_succeeded = true; } } else { - qemu_irq_intercept_in(ngl->in, qtest_irq_handler, - ngl->num_in); - interception_succeeded = true; + if (irq_intercept_dev_in) { + if (irq_intercept_dev_in == dev) { + interception_succeeded = true; + } else { + interception_duplicated = true; + } + } else { + qemu_irq_intercept_in(ngl->in, qtest_irq_handler, + ngl->num_in); + irq_intercept_dev_in = dev; + interception_succeeded = true; + } } } qtest_send_prefix(chr); if (interception_succeeded) { - irq_intercept_dev = dev; qtest_send(chr, "OK\n"); + } else if (interception_duplicated) { + qtest_send(chr, "FAIL IRQ intercept already enabled\n"); } else { qtest_send(chr, "FAIL No intercepts installed\n"); } diff --git a/tests/qtest/libqtest.c b/tests/qtest/libqtest.c index 1326e34291..754c78dad7 100644 --- a/tests/qtest/libqtest.c +++ b/tests/qtest/libqtest.c @@ -83,6 +83,7 @@ struct QTestState int expected_status; bool big_endian; bool irq_level[MAX_IRQ]; + bool is_delivered[MAX_IRQ]; GString *rx; QTestTransportOps ops; GList *pending_events; @@ -499,6 +500,7 @@ static QTestState *qtest_init_internal(const char *qemu_bin, s->rx = g_string_new(""); for (i = 0; i < MAX_IRQ; i++) { s->irq_level[i] = false; + s->is_delivered[i] = false; } /* @@ -696,6 +698,8 @@ redo: if (strcmp(words[1], "raise") == 0) { s->irq_level[irq] = true; + } else if (strcmp(words[1], "delivered") == 0) { + s->is_delivered[irq] = true; } else { s->irq_level[irq] = false; } @@ -988,6 +992,11 @@ bool qtest_get_irq(QTestState *s, int num) return s->irq_level[num]; } +bool qtest_irq_delivered(QTestState *s, int num) +{ + return s->is_delivered[num]; +} + void qtest_module_load(QTestState *s, const char *prefix, const char *libname) { qtest_sendf(s, "module_load %s %s\n", prefix, libname); diff --git a/tests/qtest/libqtest.h b/tests/qtest/libqtest.h index c261b7e0b3..6bd0ada1b2 100644 --- a/tests/qtest/libqtest.h +++ b/tests/qtest/libqtest.h @@ -366,6 +366,15 @@ void qtest_module_load(QTestState *s, const char *prefix, const char *libname); */ bool qtest_get_irq(QTestState *s, int num); +/** + * qtest_irq_delivered: + * @s: #QTestState instance to operate on. + * @num: Interrupt to observe. + * + * Returns: Is @num interrupt delivered or not. + */ +bool qtest_irq_delivered(QTestState *s, int num); + /** * qtest_irq_intercept_in: * @s: #QTestState instance to operate on. diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index 2f0d3ef080..3170c1b386 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -258,7 +258,8 @@ qtests_s390x = \ 'migration-test'] qtests_riscv32 = \ - (config_all_devices.has_key('CONFIG_SIFIVE_E_AON') ? ['sifive-e-aon-watchdog-test'] : []) + (config_all_devices.has_key('CONFIG_SIFIVE_E_AON') ? ['sifive-e-aon-watchdog-test'] : []) + \ + (config_all_devices.has_key('CONFIG_RISCV_CLIC') ? ['riscv32-clic-test'] : []) qtests_riscv64 = \ (unpack_edk2_blobs ? ['bios-tables-test'] : []) diff --git a/tests/qtest/riscv32-clic-test.c b/tests/qtest/riscv32-clic-test.c new file mode 100644 index 0000000000..7b0102142f --- /dev/null +++ b/tests/qtest/riscv32-clic-test.c @@ -0,0 +1,1928 @@ +/* + * QTest testcase for the RISC-V CLIC (Core Local Interrupt Controller) + * + * Copyright (c) 2021 T-Head Semiconductor Co., Ltd. All rights reserved. + * Copyright (c) 2024 Cirrus Logic, Inc + * and Cirrus Logic International Semiconductor Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, see . + */ + +#include "qemu/osdep.h" +#include "qemu/cutils.h" +#include "libqtest-single.h" + +/* + * Standard arguments to qtest_start. + * This finishes with the machine so that machine parameters can be passed + * by appending the string with them. E.g. QEMU_BASE_ARGS ",clic-mode=m". + * QEMU_ARGS makes use of this to give more normal-looking code. + */ +#define QEMU_BASE_ARGS "-bios none -cpu rv32 -d guest_errors " \ + "-machine virt,clic=on" +#define QEMU_ARGS(_machine_params) QEMU_BASE_ARGS _machine_params + +/* + * CLIC register addresses. + * The spec doesn't define a memory layout, other than to say that each + * CLIC should be on a 4KiB boundary if memory-mapped. + * This implementation makes all the CLICs contiguous, in the order M, S, U, + * and assumes the worst-case size. If there is only PRV_M and PRV_U, the PRV_U + * registers will appear instead of the PRV_S. + */ +#define VIRT_CLIC_MAX_IRQS 0x1000 +#define VIRT_CLIC_CONTEXT_BASE 0x1000 +#define VIRT_CLIC_INT_SIZE(_irq_count) ((_irq_count) * 4) +#define VIRT_CLIC_BLOCK_SIZE \ + (VIRT_CLIC_CONTEXT_BASE + VIRT_CLIC_INT_SIZE(VIRT_CLIC_MAX_IRQS)) + +#define VIRT_CLIC_MMODE_BASE 0x2000000 +#define VIRT_CLIC_SMODE_BASE (VIRT_CLIC_MMODE_BASE + VIRT_CLIC_BLOCK_SIZE) +#define VIRT_CLIC_UMODE_BASE (VIRT_CLIC_SMODE_BASE + VIRT_CLIC_BLOCK_SIZE) + +#define MCLICCFG_ADDR (VIRT_CLIC_MMODE_BASE + 0) +#define MCLICINFO_ADDR (VIRT_CLIC_MMODE_BASE + 4) +#define SCLICCFG_ADDR (VIRT_CLIC_SMODE_BASE + 0) +#define SCLICINFO_ADDR (VIRT_CLIC_SMODE_BASE + 4) +#define UCLICCFG_ADDR (VIRT_CLIC_UMODE_BASE + 0) +#define UCLICINFO_ADDR (VIRT_CLIC_UMODE_BASE + 4) + +/* + * Generate control register addresses for an irq + * + * @param irq_num IRQ to generate the addresses for + * + * Defines symbolic names for the clicintip, clicintie, clicintattr and + * clicintctl registers for interrupt irq_num. + */ +#define GEN_CLIC_IRQ_REG(irq_num) \ + uint64_t clicint##irq_num##_addr = \ + VIRT_CLIC_MMODE_BASE + 0x1000 + 4 * irq_num; \ + uint64_t clicintip##irq_num##_addr = \ + VIRT_CLIC_MMODE_BASE + 0x1000 + 4 * irq_num; \ + uint64_t clicintie##irq_num##_addr = \ + VIRT_CLIC_MMODE_BASE + 0x1001 + 4 * irq_num; \ + uint64_t clicintattr##irq_num##_addr = \ + VIRT_CLIC_MMODE_BASE + 0x1002 + 4 * irq_num; \ + uint64_t clicintctl##irq_num##_addr = \ + VIRT_CLIC_MMODE_BASE + 0x1003 + 4 * irq_num; \ + uint64_t clicint##irq_num##_addr_s = \ + VIRT_CLIC_SMODE_BASE + 0x1000 + 4 * irq_num; \ + uint64_t clicintip##irq_num##_addr_s = \ + VIRT_CLIC_SMODE_BASE + 0x1000 + 4 * irq_num; \ + uint64_t clicintie##irq_num##_addr_s = \ + VIRT_CLIC_SMODE_BASE + 0x1001 + 4 * irq_num; \ + uint64_t clicintattr##irq_num##_addr_s = \ + VIRT_CLIC_SMODE_BASE + 0x1002 + 4 * irq_num; \ + uint64_t clicintctl##irq_num##_addr_s = \ + VIRT_CLIC_SMODE_BASE + 0x1003 + 4 * irq_num; \ + uint64_t clicint##irq_num##_addr_u = \ + VIRT_CLIC_UMODE_BASE + 0x1000 + 4 * irq_num; \ + uint64_t clicintip##irq_num##_addr_u = \ + VIRT_CLIC_UMODE_BASE + 0x1000 + 4 * irq_num; \ + uint64_t clicintie##irq_num##_addr_u = \ + VIRT_CLIC_UMODE_BASE + 0x1001 + 4 * irq_num; \ + uint64_t clicintattr##irq_num##_addr_u = \ + VIRT_CLIC_UMODE_BASE + 0x1002 + 4 * irq_num; \ + uint64_t clicintctl##irq_num##_addr_u = \ + VIRT_CLIC_UMODE_BASE + 0x1003 + 4 * irq_num; + +/* test variable for configure case and we use 12 irq to test */ +GEN_CLIC_IRQ_REG(12) + +/* test variable for interrupt case we use irq 25 and irq 26 to test */ +GEN_CLIC_IRQ_REG(25) +GEN_CLIC_IRQ_REG(26) + +/* + * Register decodes + */ +#define INTIP_SHIFT 0 +#define INTIE_SHIFT 8 +#define INTATTR_SHIFT 16 +#define INTCTL_SHIFT 24 + +/* CLICCFG field definitions */ +#define MNL_MASK 0x0000000f +#define MNL_SHIFT 0 +#define NMBITS_MASK_1 0x00000000 /* Only PRV_M mode */ +#define NMBITS_MASK_2 0x00000010 /* PRV_M plus either PRV_S or PRV_U */ +#define NMBITS_MASK_3 0x00000030 /* PRV_M, PRV_S and PRV_U */ +#define NMBITS_SHIFT 4 +#define SNL_MASK 0x000f0000 +#define SNL_SHIFT 16 +#define UNL_MASK 0x0f000000 +#define UNL_SHIFT 24 + +/* The bits available in the different privilege modes */ +#define MCFG_MASK_1 (MNL_MASK | NMBITS_MASK_1) +#define MCFG_MASK_2 (MNL_MASK | NMBITS_MASK_2) +#define MCFG_MASK_3 (MNL_MASK | NMBITS_MASK_3) +#define MCFG_MASK MCFG_MASK_1 +#define SCFG_MASK SNL_MASK +#define UCFG_MASK UNL_MASK +#define SUCFG_MASK (SCFG_MASK | UCFG_MASK) +#define MUCFG_MASK (MCFG_MASK_2 | UCFG_MASK) +#define MSCFG_MASK (MCFG_MASK_2 | SCFG_MASK) +#define MSUCFG_MASK (MCFG_MASK_3 | SCFG_MASK | UCFG_MASK) + +/* CLICINTATTR field definitions */ +#define INTATTR_SHV 0x1 +enum INTATTR_TRIG { + TRIG_LEVEL = 0b00, + TRIG_EDGE = 0b01, + TRIG_POS = 0b00, + TRIG_NEG = 0b10, + + TRIG_HIGH = TRIG_LEVEL | TRIG_POS, + TRIG_LOW = TRIG_LEVEL | TRIG_NEG, + TRIG_RISING = TRIG_EDGE | TRIG_POS, + TRIG_FALLING = TRIG_EDGE | TRIG_NEG, +}; +enum INTATTR_MODE { + PRV_U = 0, + PRV_S = 1, + PRV_M = 3 +}; +#define INTATTR_TRIG_MASK 0x06 +#define INTATTR_TRIG_SHIFT 1 +#define INTATTR_MODE_MASK 0xC0 +#define INTATTR_MODE_SHIFT 6 + +/* Convert the byte register definitions to the 32-bit register */ +#define REG_INTIP 0x00000001 +#define REG_INTIE 0x00000100 +#define REG_SHV (INTATTR_SHV << INTATTR_SHIFT) +#define REG_TRIG_MASK (INTATTR_TRIG_MASK << INTATTR_SHIFT) +#define REG_TRIG_SHIFT (INTATTR_TRIG_SHIFT + INTATTR_SHIFT) +#define REG_MODE_MASK (INTATTR_MODE_MASK << INTATTR_SHIFT) +#define REG_MODE_SHIFT (INTATTR_MODE_SHIFT + INTATTR_SHIFT) +#define REG_INTCTL_MASK (0xff << INTCTL_SHIFT) + +/* + * Some test values, based on nmbits (_nmb) + */ +#define TEST_CFG(_nmb) ((7 << UNL_SHIFT) | (7 << SNL_SHIFT) | \ + (7 << MNL_SHIFT) | ((_nmb) << NMBITS_SHIFT)) +#define TEST_CFG_M(_nmb) (TEST_CFG(_nmb) & MCFG_MASK) /* PRV_M only */ +#define TEST_CFG_S(_nmb) (TEST_CFG(_nmb) & SCFG_MASK) /* PRV_S in MS */ +#define TEST_CFG_U(_nmb) (TEST_CFG(_nmb) & UCFG_MASK) /* PRV_U */ +#define TEST_CFG_SU(_nmb) (TEST_CFG(_nmb) & SUCFG_MASK) /* PRV_S in MSU */ +#define TEST_CFG_MU(_nmb) (TEST_CFG(_nmb) & MUCFG_MASK) /* PRV_M in MU */ +#define TEST_CFG_MS(_nmb) (TEST_CFG(_nmb) & MSCFG_MASK) /* PRV_M in MS */ +#define TEST_CFG_MSU(_nmb) (TEST_CFG(_nmb) & MSUCFG_MASK) /* PRV_M in MSU */ + +/* + * Generate a test function + * + * This writes to the given register, reads it back, and checks it has the + * expected value (which may be different from the write). + * + * @param CASE_NAME test case name + * @param WIDTH register access width - one of b, w, l or q + * @param WIDTH_BITS value width in bits + * @param reg_addr register to write and read + * @param set_value value to write to the register + * @param expected expected value to read back from the register + */ +#define GEN_CHECK_REG_MMIO(CASE_NAME, WIDTH, WIDTH_BITS, reg_addr, \ + set_value, expected) \ +static void test_configure_##CASE_NAME(void) \ +{ \ + uint##WIDTH_BITS##_t _set_value = set_value; \ + uint##WIDTH_BITS##_t _expected = expected; \ + write##WIDTH(reg_addr, _set_value); \ + uint##WIDTH_BITS##_t result = read##WIDTH(reg_addr); \ + g_assert_cmpuint(result, ==, _expected); \ +} + +/* test function for an 8-bit value */ +#define GEN_CHECK_REG_MMIO_B(CASE_NAME, reg_addr, set_value, expected) \ + GEN_CHECK_REG_MMIO(CASE_NAME, b, 8, reg_addr, set_value, expected) +/* test function for a 16-bit value */ +#define GEN_CHECK_REG_MMIO_W(CASE_NAME, reg_addr, set_value, expected) \ + GEN_CHECK_REG_MMIO(CASE_NAME, w, 16, reg_addr, set_value, expected) +/* test function for a 32-bit value */ +#define GEN_CHECK_REG_MMIO_L(CASE_NAME, reg_addr, set_value, expected) \ + GEN_CHECK_REG_MMIO(CASE_NAME, l, 32, reg_addr, set_value, expected) +/* test function for a 64-bit value */ +#define GEN_CHECK_REG_MMIO_Q(CASE_NAME, reg_addr, set_value, expected) \ + GEN_CHECK_REG_MMIO(CASE_NAME, q, 64, reg_addr, set_value, expected) + + + /* test case definitions */ + +/* + * cliccfg tests + * + * Layout: + * 31:28 reserved (WPRI 0) + * 27:24 unlbits + * 23:20 reserved (WPRI 0) + * 19:16 snlbits + * 15:6 reserved (WPRI 0) + * 5:4 nmbits + * 3:0 mnlbits + */ + +/* + * Set the minimum mnlbits + * set nmbits = 0, mnlbits = 0, snlbits = 0, unlbits = 0 + */ +GEN_CHECK_REG_MMIO_L(cliccfg_min_mnlbits, MCLICCFG_ADDR, 0x0, 0x0) + +/* + * Set the max supported mnlbits + * set nmbits = 0, mnlbits = 8, snlbits = 0, unlbits = 0 + */ +GEN_CHECK_REG_MMIO_L(cliccfg_supported_max_mnlbits, MCLICCFG_ADDR, 0x8, 0x8) + +/* + * Set mnlbits to an unsupported value + * set nmbits = 0, mnlbits = 10, snlbits = 0, unlbits = 0 + */ +GEN_CHECK_REG_MMIO_L(cliccfg_unsupported_mnlbits, MCLICCFG_ADDR, 0xA, 0x8) + +/* + * Set the minimum snlbits + * set nmbits = 0, mnlbits = 4, snlbits = 0, unlbits = 0 + * Requires PRV_S mode + */ +GEN_CHECK_REG_MMIO_L(cliccfg_min_snlbits_s, MCLICCFG_ADDR, 0x00004, 0x00004) + +/* + * Set the max supported snlbits + * set nmbits = 0, mnlbits = 4, snlbits = 8, unlbits = 0 + * Requires PRV_S mode + */ +GEN_CHECK_REG_MMIO_L(cliccfg_supported_max_snlbits_s, MCLICCFG_ADDR, + 0x80004, 0x80004) + +/* + * Set snlbits to an unsupported value + * set nmbits = 0, mnlbits = 4, snlbits = 10, unlbits = 0 + * Requires PRV_S mode + */ +GEN_CHECK_REG_MMIO_L(cliccfg_unsupported_snlbits_s, MCLICCFG_ADDR, + 0xA0004, 0x80004) + +/* + * Set snlbits with no PRV_S support + * set nmbits = 0, mnlbits = 4, snlbits = 8, unlbits = 0 + */ +GEN_CHECK_REG_MMIO_L(cliccfg_snlbits_no_s, MCLICCFG_ADDR, 0x80004, 0x00004) + +/* + * Set the minimum unlbits + * set nmbits = 0, mnlbits = 4, snlbits = 0, unlbits = 0 + * Requires PRV_S mode + */ +GEN_CHECK_REG_MMIO_L(cliccfg_min_unlbits_u, MCLICCFG_ADDR, 0x0000004, 0x0000004) + +/* + * Set the max supported unlbits + * set nmbits = 0, mnlbits = 4, snlbits = 0, unlbits = 8 + * Requires PRV_U mode + */ +GEN_CHECK_REG_MMIO_L(cliccfg_supported_max_unlbits_u, MCLICCFG_ADDR, + 0x8000004, 0x8000004) + +/* + * Set unlbits to an unsupported value + * set nmbits = 0, mnlbits = 4, snlbits = 0, unlbits = 10 + * Requires PRV_U mode + */ +GEN_CHECK_REG_MMIO_L(cliccfg_unsupported_unlbits_u, MCLICCFG_ADDR, + 0xA000004, 0x8000004) + +/* + * Set unlbits with no PRV_U support + * set nmbits = 0, mnlbits = 4, snlbits = 0, unlbits = 8 + */ +GEN_CHECK_REG_MMIO_L(cliccfg_unlbits_no_u, MCLICCFG_ADDR, 0x8000004, 0x0000004) + +/* + * Set all modes + * set nmbits = 0, mnlbits = 4, snlbits = 2, unlbits = 2 + * Requires PRV_S + PRV_U mode + */ +GEN_CHECK_REG_MMIO_L(cliccfg_xnlbits, MCLICCFG_ADDR, 0x2020004, 0x2020004) + +/* + * nmbits = 0 + * set nmbits = 0, mnlbits = 8 + */ +GEN_CHECK_REG_MMIO_L(cliccfg_nmbits_0, MCLICCFG_ADDR, 0x08, 0x08) + +/* + * nmbits = 1 needs PRV_S or PRV_U + * set nmbits = 1, mnlbits = 8 + */ +GEN_CHECK_REG_MMIO_L(cliccfg_nmbits_1, MCLICCFG_ADDR, 0x18, 0x18) +GEN_CHECK_REG_MMIO_L(cliccfg_unsupported_nmbits_1, MCLICCFG_ADDR, 0x18, 0x08) + +/* + * nmbits = 2 needs PRV_S and PRV_U + * set nmbits = 2, mnlbits = 8 + */ +GEN_CHECK_REG_MMIO_L(cliccfg_nmbits_2, MCLICCFG_ADDR, 0x28, 0x28) +GEN_CHECK_REG_MMIO_L(cliccfg_unsupported_nmbits_2, MCLICCFG_ADDR, 0x28, 0x08) + +/* + * nmbits = 3 is not supported + * set nmbits = 3, mnlbits = 8 + */ +GEN_CHECK_REG_MMIO_L(cliccfg_unsupported_nmbits_3, MCLICCFG_ADDR, 0x38, 0x08) + +/* + * clicintie tests + * + * Layout: + * [0] enable: 1 = enabled, 0 = disabled + */ + +/* set clicintie[i] = 0x1 and compare */ +GEN_CHECK_REG_MMIO_B(clicintie_enable, clicintie12_addr, 0x1, 0x1) + +/* set clicintie[i] = 0x0 and compare */ +GEN_CHECK_REG_MMIO_B(clicintie_disable, clicintie12_addr, 0, 0) + +/* + * clicintattr tests + * + * Layout: + * [7:6] mode b00 = U, b01 = S, b10 = reserved, b11 = M + * [5:3] reserved + * [2:1] trig trig[0]: 0 = level, 1 = edge; trig[1]: 0 = pos, 1 = neg + * [0] shv 0 = non-vectored, 1 = vectored + */ + +/* Mode tests - note these deliberately use different trig and int settings */ +/* + * Set mode 3 - PRV_M + * mode = b11, trig = b11, shv = b1 + * clicintattr[i] = 0xc7 + * expected + * mode = b11, tri = b11, shv = b1 + * clicintattr[i] = 0xc7 + */ +GEN_CHECK_REG_MMIO_B(clicintattr_prv_m, clicintattr12_addr, + 0xc7, 0xc7) + +/* + * Set mode 1 - PRV_S + * mode = b01, trig = b10, shv = b0 + * clicintattr[i] = 0x44 + * expected + * mode = b01, tri = b10, shv = b0 + * clicintattr[i] = 0x44 + */ +GEN_CHECK_REG_MMIO_B(clicintattr_prv_s_supported, clicintattr12_addr, + 0x44, 0x44) + +/* + * Set mode 0 - PRV_U + * mode = b00, trig = b01, shv = b1 + * clicintattr[i] = 0x03 + * expected + * mode = b00, tri = b01, shv = b1 + * clicintattr[i] = 0x03 + */ +GEN_CHECK_REG_MMIO_B(clicintattr_prv_u_supported, clicintattr12_addr, + 0x03, 0x03) + +/* + * WARL: clicintattr should return PRV_M for PRV_S if PRV_U and PRV_S are + * both unsupported or nmbits = 0. + * + * mode = b01, tri = b10, shv = b0 + * clicintattr[i] = 0x44 + * expected + * mode = b11, tri = b10, shv = b0 + * clicintattr[i] = 0xc4 + */ +GEN_CHECK_REG_MMIO_B(clicintattr_prv_s_to_m_warl, clicintattr12_addr, + 0x44, 0xc4) + +/* + * WARL: clicintattr should return PRV_U for PRV_S if PRV_S is unsupported and + * nmbits = 1. + * + * mode = b01, tri = b10, shv = b0 + * clicintattr[i] = 0x44 + * expected + * mode = b11, tri = b10, shv = b0 + * clicintattr[i] = 0x04 + */ +GEN_CHECK_REG_MMIO_B(clicintattr_prv_s_to_u_warl, clicintattr12_addr, + 0x44, 0x04) + +/* + * WARL: clicintattr should return PRV_M for PRV_U if PRV_U and PRV_S are + * both unsupported or nmbits = 0. + * + * mode = b00, tri = b01, shv = b1 + * clicintattr[i] = 0x03 + * expected + * mode = b11, tri = b01, shv = b1 + * clicintattr[i] = 0xc3 + */ +GEN_CHECK_REG_MMIO_B(clicintattr_prv_u_to_m_warl, clicintattr12_addr, + 0x03, 0xc3) + +/* + * WARL: clicintattr should return PRV_S for PRV_U if PRV_U is unsupported and + * nmbits = 1. + * + * mode = b00, tri = b01, shv = b1 + * clicintattr[i] = 0x03 + * expected + * mode = b01, tri = b01, shv = b1 + * clicintattr[i] = 0x43 + */ +GEN_CHECK_REG_MMIO_B(clicintattr_prv_u_to_s_warl, clicintattr12_addr, + 0x03, 0x43) + +/* + * Mode 2 is invalid + * mode = b10, trig = b00, shv = b0 + * clicintattr[i] = 0x80 + * expected + * mode = b11, tri = b00, shv = b1 + * clicintattr[i] = 0xc1 + */ +GEN_CHECK_REG_MMIO_B(clicintattr_unsupported_mode_10, clicintattr12_addr, + 0x81, 0xc1) + +/* + * set positive edge-triggered, vectored + * mode = b11, tri = b01, shv = b1 + * clicintattr[i] = 0xc1 + * expected + * mode = b11, tri = b01, shv = b1 + * clicintattr[i] = 0xc1 + */ +GEN_CHECK_REG_MMIO_B(clicintattr_positive_edge_triggered, clicintattr12_addr, + 0xc3, 0xc3) + +/* + * set negative edge-triggered, vectored + * mode = b11, tri = b11, shv = b1 + * clicintattr[i] = 0xc7 + * expected + * mode = b11, tri = b11, shv = b1 + * clicintattr[i] = 0xc7 + */ +GEN_CHECK_REG_MMIO_B(clicintattr_negative_edge_triggered, clicintattr12_addr, + 0xc7, 0xc7) + +/* + * set positive level-triggered, vectored + * mode = b11, tri = b00, shv = b1 + * clicintattr[i] = 0xc1 + * expected + * mode = b11, tri = b00, shv = b1 + * clicintattr[i] = 0xc1 + */ +GEN_CHECK_REG_MMIO_B(clicintattr_positive_level_triggered, clicintattr12_addr, + 0xc1, 0xc1) + +/* + * set negative level-triggered, vectored + * mode = b11, tri = b10, shv = b1 + * clicintattr[i] = 0xc5 + * expected + * mode = b11, tri = b00, shv = b1 + * clicintattr[i] = 0xc5 + */ +GEN_CHECK_REG_MMIO_B(clicintattr_negative_level_triggered, clicintattr12_addr, + 0xc5, 0xc5) + +/* + * set non-vectored + * mode = b11, tri = b11, shv = b0 + * clicintattr[i] = 0xc6 + * expected + * mode = b11, tri = b11, shv = b0 + * clicintattr[i] = 0xc6 + */ +GEN_CHECK_REG_MMIO_B(clicintattr_non_vectored, clicintattr12_addr, + 0xc6, 0xc6) + +/* + * clicintctl tests + * + * Layout depends on mnlbits/snlbits/unlbits in mcliccfg. + * + */ + +/* + * Test with 0 intctlbits - mask 0xff + * Everything rounds up + * clicintctl[i] = 0xFF + */ +GEN_CHECK_REG_MMIO_B(clicintctl_set_0_0_bits, clicintctl12_addr, + 0x00, 0xff) +GEN_CHECK_REG_MMIO_B(clicintctl_set_33_0_bits, clicintctl12_addr, + 0x21, 0xff) +GEN_CHECK_REG_MMIO_B(clicintctl_set_88_0_bits, clicintctl12_addr, + 0x58, 0xff) +GEN_CHECK_REG_MMIO_B(clicintctl_set_128_0_bits, clicintctl12_addr, + 0x80, 0xff) +GEN_CHECK_REG_MMIO_B(clicintctl_set_204_0_bits, clicintctl12_addr, + 0xcc, 0xff) +GEN_CHECK_REG_MMIO_B(clicintctl_set_240_0_bits, clicintctl12_addr, + 0xf0, 0xff) + +/* + * Test with 1 intctlbit - mask 0x7f + * The top bit is used, everything else rounds up + */ +GEN_CHECK_REG_MMIO_B(clicintctl_set_0_1_bits, clicintctl12_addr, + 0x00, 0x7f) +GEN_CHECK_REG_MMIO_B(clicintctl_set_33_1_bits, clicintctl12_addr, + 0x21, 0x7f) +GEN_CHECK_REG_MMIO_B(clicintctl_set_88_1_bits, clicintctl12_addr, + 0x58, 0x7f) +GEN_CHECK_REG_MMIO_B(clicintctl_set_128_1_bits, clicintctl12_addr, + 0x80, 0xff) +GEN_CHECK_REG_MMIO_B(clicintctl_set_204_1_bits, clicintctl12_addr, + 0xcc, 0xff) +GEN_CHECK_REG_MMIO_B(clicintctl_set_240_1_bits, clicintctl12_addr, + 0xf0, 0xff) + +/* + * Test with 2 intctlbits - mask 0x3f + * The top 2 bits are used, everything else rounds up + */ +GEN_CHECK_REG_MMIO_B(clicintctl_set_0_2_bits, clicintctl12_addr, + 0x00, 0x3f) +GEN_CHECK_REG_MMIO_B(clicintctl_set_33_2_bits, clicintctl12_addr, + 0x21, 0x3f) +GEN_CHECK_REG_MMIO_B(clicintctl_set_88_2_bits, clicintctl12_addr, + 0x58, 0x7f) +GEN_CHECK_REG_MMIO_B(clicintctl_set_128_2_bits, clicintctl12_addr, + 0x80, 0xbf) +GEN_CHECK_REG_MMIO_B(clicintctl_set_204_2_bits, clicintctl12_addr, + 0xcc, 0xff) +GEN_CHECK_REG_MMIO_B(clicintctl_set_240_2_bits, clicintctl12_addr, + 0xf0, 0xff) + +/* + * Test with 3 intctlbits - mask 0x1f + * The top 3 bits are used, everything else rounds up + */ +GEN_CHECK_REG_MMIO_B(clicintctl_set_0_3_bits, clicintctl12_addr, + 0x00, 0x1f) +GEN_CHECK_REG_MMIO_B(clicintctl_set_33_3_bits, clicintctl12_addr, + 0x21, 0x3f) +GEN_CHECK_REG_MMIO_B(clicintctl_set_88_3_bits, clicintctl12_addr, + 0x58, 0x5f) +GEN_CHECK_REG_MMIO_B(clicintctl_set_128_3_bits, clicintctl12_addr, + 0x80, 0x9f) +GEN_CHECK_REG_MMIO_B(clicintctl_set_204_3_bits, clicintctl12_addr, + 0xcc, 0xdf) +GEN_CHECK_REG_MMIO_B(clicintctl_set_240_3_bits, clicintctl12_addr, + 0xf0, 0xff) + +/* + * Test with 4 intctlbits - mask 0x0f + * The top 4 bits are used, everything else rounds up + */ +GEN_CHECK_REG_MMIO_B(clicintctl_set_0_4_bits, clicintctl12_addr, + 0x00, 0x0f) +GEN_CHECK_REG_MMIO_B(clicintctl_set_33_4_bits, clicintctl12_addr, + 0x21, 0x2f) +GEN_CHECK_REG_MMIO_B(clicintctl_set_88_4_bits, clicintctl12_addr, + 0x58, 0x5f) +GEN_CHECK_REG_MMIO_B(clicintctl_set_128_4_bits, clicintctl12_addr, + 0x80, 0x8f) +GEN_CHECK_REG_MMIO_B(clicintctl_set_204_4_bits, clicintctl12_addr, + 0xcc, 0xcf) +GEN_CHECK_REG_MMIO_B(clicintctl_set_240_4_bits, clicintctl12_addr, + 0xf0, 0xff) + +/* + * Test with 5 intctlbits - mask 0x07 + * The top 5 bits are used, everything else rounds up + */ +GEN_CHECK_REG_MMIO_B(clicintctl_set_0_5_bits, clicintctl12_addr, + 0x00, 0x07) +GEN_CHECK_REG_MMIO_B(clicintctl_set_33_5_bits, clicintctl12_addr, + 0x21, 0x27) +GEN_CHECK_REG_MMIO_B(clicintctl_set_88_5_bits, clicintctl12_addr, + 0x58, 0x5f) +GEN_CHECK_REG_MMIO_B(clicintctl_set_128_5_bits, clicintctl12_addr, + 0x80, 0x87) +GEN_CHECK_REG_MMIO_B(clicintctl_set_204_5_bits, clicintctl12_addr, + 0xcc, 0xcf) +GEN_CHECK_REG_MMIO_B(clicintctl_set_240_5_bits, clicintctl12_addr, + 0xf0, 0xf7) + +/* + * Test with 6 intctlbits - mask 0x03 + * The top 6 bits are used, everything else rounds up + */ +GEN_CHECK_REG_MMIO_B(clicintctl_set_0_6_bits, clicintctl12_addr, + 0x00, 0x03) +GEN_CHECK_REG_MMIO_B(clicintctl_set_33_6_bits, clicintctl12_addr, + 0x21, 0x23) +GEN_CHECK_REG_MMIO_B(clicintctl_set_88_6_bits, clicintctl12_addr, + 0x58, 0x5b) +GEN_CHECK_REG_MMIO_B(clicintctl_set_128_6_bits, clicintctl12_addr, + 0x80, 0x83) +GEN_CHECK_REG_MMIO_B(clicintctl_set_204_6_bits, clicintctl12_addr, + 0xcc, 0xcf) +GEN_CHECK_REG_MMIO_B(clicintctl_set_240_6_bits, clicintctl12_addr, + 0xf0, 0xf3) + +/* + * Test with 7 intctlbits - mask 0x01 + * The top 7 bits are used, everything else rounds up + */ +GEN_CHECK_REG_MMIO_B(clicintctl_set_0_7_bits, clicintctl12_addr, + 0x00, 0x01) +GEN_CHECK_REG_MMIO_B(clicintctl_set_33_7_bits, clicintctl12_addr, + 0x21, 0x21) +GEN_CHECK_REG_MMIO_B(clicintctl_set_88_7_bits, clicintctl12_addr, + 0x58, 0x59) +GEN_CHECK_REG_MMIO_B(clicintctl_set_128_7_bits, clicintctl12_addr, + 0x80, 0x81) +GEN_CHECK_REG_MMIO_B(clicintctl_set_204_7_bits, clicintctl12_addr, + 0xcc, 0xcd) +GEN_CHECK_REG_MMIO_B(clicintctl_set_240_7_bits, clicintctl12_addr, + 0xf0, 0xf1) + +/* + * Test with 8 intctlbits - mask 0x00 + * All bits are used + */ +GEN_CHECK_REG_MMIO_B(clicintctl_set_0_8_bits, clicintctl12_addr, + 0x00, 0x00) +GEN_CHECK_REG_MMIO_B(clicintctl_set_33_8_bits, clicintctl12_addr, + 0x21, 0x21) +GEN_CHECK_REG_MMIO_B(clicintctl_set_88_8_bits, clicintctl12_addr, + 0x58, 0x58) +GEN_CHECK_REG_MMIO_B(clicintctl_set_128_8_bits, clicintctl12_addr, + 0x80, 0x80) +GEN_CHECK_REG_MMIO_B(clicintctl_set_204_8_bits, clicintctl12_addr, + 0xcc, 0xcc) +GEN_CHECK_REG_MMIO_B(clicintctl_set_240_8_bits, clicintctl12_addr, + 0xf0, 0xf0) + +/* + * read clicintip[i] to result + * set cliccfg = 0x11, clicintattr[i] = 0x31, clicintip[i] = 0x1 + * check the value of clicintip[i] that is changed or not + */ +static void test_configure_clicintip_level_triggered_read_only(void) +{ + /* configure level-triggered mode */ + writeb(clicintattr12_addr, 0xc1); + g_assert_cmpuint(readb(clicintattr12_addr), ==, 0xc1); + + uint8_t orig_value = readb(clicintip12_addr); + writeb(clicintip12_addr, 0x1); + uint32_t result = readb(clicintip12_addr); + + g_assert_cmpuint(orig_value, ==, result); +} + +static void boot_qemu_m(void) +{ + global_qtest = qtest_start(QEMU_ARGS(",clic-mode=m")); +} + +static void boot_qemu_ms(void) +{ + global_qtest = qtest_start(QEMU_ARGS(",clic-mode=ms")); +} + +static void boot_qemu_mu(void) +{ + global_qtest = qtest_start(QEMU_ARGS(",clic-mode=mu")); +} + +static void boot_qemu_msu(void) +{ + global_qtest = qtest_start(QEMU_ARGS(",clic-mode=msu")); +} + +#define GEN_BOOT_QEMU_INTCTL(_nbits) \ +static void boot_qemu_##_nbits##_bits(void) \ +{ \ + global_qtest = qtest_initf(QEMU_BASE_ARGS \ + ",clic-mode=msu,clic-intctlbits=%d", \ + _nbits); \ +} + +static void shut_down_qemu(void) +{ + qtest_quit(global_qtest); +} + +/* + * test vectored positive level triggered interrupt + * test points: + * 1. we use interrupt 25 and 26 to test arbitration in two situation: + * same level, different level. + * 2. within level triggered mode, we can only use device to clear pending. + * 3. within positive level triggered mode, set gpio-in rise + * to trigger interrupt. + */ +static void test_vectored_positive_level_triggered_interrupt(void) +{ + QTestState *qts = qtest_start(QEMU_BASE_ARGS); + /* intercept in and out irq */ + qtest_irq_intercept_out(qts, "/machine/unattached/device[1]"); + qtest_irq_intercept_in(qts, "/machine/unattached/device[1]"); + + /* configure */ + qtest_writeb(qts, MCLICCFG_ADDR, 0x1); + qtest_writeb(qts, clicintattr25_addr, 0xc1); + qtest_writeb(qts, clicintattr26_addr, 0xc1); + g_assert_cmpuint(qtest_readb(qts, clicintip25_addr), ==, 0); + g_assert_cmpuint(qtest_readb(qts, clicintip26_addr), ==, 0); + + /* set pending */ + /* arbitration will be made and 26 will be delivered */ + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 1); + g_assert_cmpuint(qtest_readb(qts, clicintip25_addr), ==, 1); + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 26, 1); + g_assert_cmpuint(qtest_readb(qts, clicintip26_addr), ==, 1); + qtest_writeb(qts, clicintie25_addr, 1); + g_assert_cmpuint(qtest_readb(qts, clicintie25_addr), ==, 1); + qtest_writeb(qts, clicintie26_addr, 1); + g_assert_cmpuint(qtest_readb(qts, clicintie26_addr), ==, 1); + /* trigger arbitration */ + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 1); + g_assert_true(qtest_irq_delivered(qts, 26)); + g_assert_true(qtest_get_irq(qts, 0)); + + /* + * level trigger wouldn't auto clear clear pending, + * so we need to manually do it. + */ + qtest_writeb(qts, clicintie25_addr, 0); + qtest_writeb(qts, clicintie26_addr, 0); + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 0); + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 26, 0); + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 0, 0); + g_assert_cmpuint(qtest_readb(qts, clicintip25_addr), ==, 0); + g_assert_cmpuint(qtest_readb(qts, clicintip26_addr), ==, 0); + + /* set interrupt 25 level 255, interrupt 26 level 127 */ + /* arbitration will be made and 25 will rise */ + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 1); + g_assert_cmpuint(qtest_readb(qts, clicintip25_addr), ==, 1); + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 26, 1); + g_assert_cmpuint(qtest_readb(qts, clicintip26_addr), ==, 1); + qtest_writeb(qts, clicintctl25_addr, 0xbf); + qtest_writeb(qts, clicintctl26_addr, 0x3f); + qtest_writeb(qts, clicintie25_addr, 1); + g_assert_cmpuint(qtest_readb(qts, clicintie25_addr), ==, 1); + qtest_writeb(qts, clicintie26_addr, 1); + g_assert_cmpuint(qtest_readb(qts, clicintie26_addr), ==, 1); + /* trigger arbitration */ + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 1); + g_assert_true(qtest_irq_delivered(qts, 25)); + g_assert_true(qtest_get_irq(qts, 0)); + + qtest_quit(qts); +} + +/* + * test vectored negative level triggered interrupt + * test points: + * 1. we use interrupt 25 and 26 to test arbitration in two situation: + * same level, different level. + * 2. within level triggered mode, we can only use device to clear pending. + * 3. within negative level triggered mode, + * set gpio-in lower to trigger interrupt. + */ +static void test_vectored_negative_level_triggered_interrupt(void) +{ + QTestState *qts = qtest_start(QEMU_BASE_ARGS); + /* intercept in and out irq */ + qtest_irq_intercept_out(qts, "/machine/unattached/device[1]"); + qtest_irq_intercept_in(qts, "/machine/unattached/device[1]"); + + /* configure */ + qtest_writeb(qts, MCLICCFG_ADDR, 0x1); + qtest_writeb(qts, clicintattr25_addr, 0xc5); + qtest_readb(qts, clicintattr25_addr); + qtest_writeb(qts, clicintattr26_addr, 0xc5); + qtest_readb(qts, clicintattr26_addr); + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 1); + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 26, 1); + g_assert_cmpuint(qtest_readb(qts, clicintip25_addr), ==, 0); + g_assert_cmpuint(qtest_readb(qts, clicintip26_addr), ==, 0); + + /* set pending */ + /* arbitration will be made and 26 will be delivered */ + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 0); + g_assert_cmpuint(qtest_readb(qts, clicintip25_addr), ==, 1); + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 26, 0); + g_assert_cmpuint(qtest_readb(qts, clicintip26_addr), ==, 1); + qtest_writeb(qts, clicintie25_addr, 1); + g_assert_cmpuint(qtest_readb(qts, clicintie25_addr), ==, 1); + qtest_writeb(qts, clicintie26_addr, 1); + g_assert_cmpuint(qtest_readb(qts, clicintie26_addr), ==, 1); + /* trigger arbitration */ + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 0); + g_assert_true(qtest_irq_delivered(qts, 26)); + g_assert_true(qtest_get_irq(qts, 0)); + + /* + * level trigger wouldn't auto clear clear pending, + * so we need to manually do it. + */ + qtest_writeb(qts, clicintie25_addr, 0); + qtest_writeb(qts, clicintie26_addr, 0); + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 1); + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 26, 1); + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 0, 0); + g_assert_cmpuint(qtest_readb(qts, clicintip25_addr), ==, 0); + g_assert_cmpuint(qtest_readb(qts, clicintip26_addr), ==, 0); + + /* set interrupt 25 level 255, interrupt 26 level 127 */ + /* arbitration will be made and 25 will rise */ + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 0); + g_assert_cmpuint(qtest_readb(qts, clicintip25_addr), ==, 1); + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 26, 0); + g_assert_cmpuint(qtest_readb(qts, clicintip26_addr), ==, 1); + qtest_writeb(qts, clicintctl25_addr, 0xbf); + qtest_writeb(qts, clicintctl26_addr, 0x3f); + qtest_writeb(qts, clicintie25_addr, 1); + g_assert_cmpuint(qtest_readb(qts, clicintie25_addr), ==, 1); + qtest_writeb(qts, clicintie26_addr, 1); + g_assert_cmpuint(qtest_readb(qts, clicintie26_addr), ==, 1); + /* trigger arbitration */ + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 0); + g_assert_true(qtest_irq_delivered(qts, 25)); + g_assert_true(qtest_get_irq(qts, 0)); + + qtest_quit(qts); +} + +/* + * test vectored positive edge triggered interrupt + * test points: + * 1. we use interrupt 25 and 26 to test arbitration in two situation: + * same level, different level. + * 2. within vectored edge triggered mode, pending bit will be + * automatically cleared. + * 3. within positive edge triggered mode, set gpio-in from + * lower to rise to trigger interrupt. + */ +static void test_vectored_positive_edge_triggered_interrupt(void) +{ + QTestState *qts = qtest_start(QEMU_BASE_ARGS); + /* intercept in and out irq */ + qtest_irq_intercept_out(qts, "/machine/unattached/device[1]"); + qtest_irq_intercept_in(qts, "/machine/unattached/device[1]"); + + /* configure */ + qtest_writeb(qts, MCLICCFG_ADDR, 0x1); + qtest_writeb(qts, clicintattr25_addr, 0xc3); + qtest_writeb(qts, clicintattr26_addr, 0xc3); + g_assert_cmpuint(qtest_readb(qts, clicintip25_addr), ==, 0); + g_assert_cmpuint(qtest_readb(qts, clicintip26_addr), ==, 0); + + /* set pending */ + /* arbitration will be made and 26 will be delivered */ + qtest_writeb(qts, clicintip25_addr, 1); + g_assert_cmpuint(qtest_readb(qts, clicintip25_addr), ==, 1); + qtest_writeb(qts, clicintip26_addr, 1); + g_assert_cmpuint(qtest_readb(qts, clicintip26_addr), ==, 1); + qtest_writeb(qts, clicintie25_addr, 1); + g_assert_cmpuint(qtest_readb(qts, clicintie25_addr), ==, 1); + qtest_writeb(qts, clicintie26_addr, 1); + g_assert_cmpuint(qtest_readb(qts, clicintie26_addr), ==, 1); + /* trigger arbitration */ + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 1); + g_assert_true(qtest_irq_delivered(qts, 26)); + g_assert_true(qtest_get_irq(qts, 0)); + + /* vectored edge trigger will auto clear clear pending */ + qtest_writeb(qts, clicintie25_addr, 0); + qtest_writeb(qts, clicintie26_addr, 0); + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 0); + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 0, 0); + g_assert_cmpuint(qtest_readb(qts, clicintip25_addr), ==, 0); + g_assert_cmpuint(qtest_readb(qts, clicintip26_addr), ==, 0); + + /* set interrupt 25 level 255, interrupt 26 level 127 */ + /* arbitration will be made and 25 will rise */ + qtest_writeb(qts, clicintip25_addr, 1); + g_assert_cmpuint(qtest_readb(qts, clicintip25_addr), ==, 1); + qtest_writeb(qts, clicintip26_addr, 1); + g_assert_cmpuint(qtest_readb(qts, clicintip26_addr), ==, 1); + qtest_writeb(qts, clicintctl25_addr, 0xbf); + qtest_writeb(qts, clicintctl26_addr, 0x3f); + qtest_writeb(qts, clicintie25_addr, 1); + g_assert_cmpuint(qtest_readb(qts, clicintie25_addr), ==, 1); + qtest_writeb(qts, clicintie26_addr, 1); + g_assert_cmpuint(qtest_readb(qts, clicintie26_addr), ==, 1); + /* trigger arbitration */ + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 1); + g_assert_true(qtest_irq_delivered(qts, 25)); + g_assert_true(qtest_get_irq(qts, 0)); + + qtest_quit(qts); +} + +/* + * test vectored negative edge triggered interrupt + * test points: + * 1. we use interrupt 25 and 26 to test arbitration in two situation: + * same level, different level. + * 2. within vectored edge triggered mode, pending bit will be + * automatically cleared. + * 3. within negative edge triggered mode, set gpio-in from + * rise to lower to trigger interrupt. + */ +static void test_vectored_negative_edge_triggered_interrupt(void) +{ + QTestState *qts = qtest_start(QEMU_BASE_ARGS); + /* intercept in and out irq */ + qtest_irq_intercept_out(qts, "/machine/unattached/device[1]"); + qtest_irq_intercept_in(qts, "/machine/unattached/device[1]"); + + /* configure */ + qtest_writeb(qts, MCLICCFG_ADDR, 0x1); + qtest_writeb(qts, clicintattr25_addr, 0xc7); + qtest_writeb(qts, clicintattr26_addr, 0xc7); + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 1); + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 26, 1); + g_assert_cmpuint(qtest_readb(qts, clicintip25_addr), ==, 0); + g_assert_cmpuint(qtest_readb(qts, clicintip26_addr), ==, 0); + + /* set pending */ + /* arbitration will be made and 26 will be delivered */ + qtest_writeb(qts, clicintip25_addr, 1); + g_assert_cmpuint(qtest_readb(qts, clicintip25_addr), ==, 1); + qtest_writeb(qts, clicintip26_addr, 1); + g_assert_cmpuint(qtest_readb(qts, clicintip26_addr), ==, 1); + qtest_writeb(qts, clicintie25_addr, 1); + g_assert_cmpuint(qtest_readb(qts, clicintie25_addr), ==, 1); + qtest_writeb(qts, clicintie26_addr, 1); + g_assert_cmpuint(qtest_readb(qts, clicintie26_addr), ==, 1); + /* trigger arbitration */ + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 0); + g_assert_true(qtest_irq_delivered(qts, 26)); + g_assert_true(qtest_get_irq(qts, 0)); + + /* vectored edge trigger will auto clear clear pending */ + qtest_writeb(qts, clicintie25_addr, 0); + qtest_writeb(qts, clicintie26_addr, 0); + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 1); + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 0, 0); + g_assert_cmpuint(qtest_readb(qts, clicintip25_addr), ==, 0); + g_assert_cmpuint(qtest_readb(qts, clicintip26_addr), ==, 0); + + /* set interrupt 25 level 255, interrupt 26 level 127 */ + /* arbitration will be made and 25 will rise */ + qtest_writeb(qts, clicintip25_addr, 1); + g_assert_cmpuint(qtest_readb(qts, clicintip25_addr), ==, 1); + qtest_writeb(qts, clicintip26_addr, 1); + g_assert_cmpuint(qtest_readb(qts, clicintip26_addr), ==, 1); + qtest_writeb(qts, clicintctl25_addr, 0xbf); + qtest_writeb(qts, clicintctl26_addr, 0x3f); + qtest_writeb(qts, clicintie25_addr, 1); + g_assert_cmpuint(qtest_readb(qts, clicintie25_addr), ==, 1); + qtest_writeb(qts, clicintie26_addr, 1); + g_assert_cmpuint(qtest_readb(qts, clicintie26_addr), ==, 1); + /* trigger arbitration */ + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 0); + g_assert_true(qtest_irq_delivered(qts, 25)); + g_assert_true(qtest_get_irq(qts, 0)); + + qtest_quit(qts); +} + +/* + * test unvectored positive level triggered interrupt + * test points: + * 1. we use interrupt 25 and 26 to test arbitration in two situation: + * same level, different level. + * 2. within level triggered mode, we can only use device to clear pending. + * 3. within positive level triggered mode, set gpio-in rise to + * trigger interrupt. + */ +static void test_unvectored_positive_level_triggered_interrupt(void) +{ + QTestState *qts = qtest_start(QEMU_BASE_ARGS); + /* intercept in and out irq */ + qtest_irq_intercept_out(qts, "/machine/unattached/device[1]"); + qtest_irq_intercept_in(qts, "/machine/unattached/device[1]"); + + /* configure */ + qtest_writeb(qts, MCLICCFG_ADDR, 0x1); + qtest_writeb(qts, clicintattr25_addr, 0xc0); + qtest_writeb(qts, clicintattr26_addr, 0xc0); + g_assert_cmpuint(qtest_readb(qts, clicintip25_addr), ==, 0); + g_assert_cmpuint(qtest_readb(qts, clicintip26_addr), ==, 0); + + /* set pending */ + /* arbitration will be made and 26 will be delivered */ + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 1); + g_assert_cmpuint(qtest_readb(qts, clicintip25_addr), ==, 1); + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 26, 1); + g_assert_cmpuint(qtest_readb(qts, clicintip26_addr), ==, 1); + qtest_writeb(qts, clicintie25_addr, 1); + g_assert_cmpuint(qtest_readb(qts, clicintie25_addr), ==, 1); + qtest_writeb(qts, clicintie26_addr, 1); + g_assert_cmpuint(qtest_readb(qts, clicintie26_addr), ==, 1); + /* trigger arbitration */ + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 1); + g_assert_true(qtest_irq_delivered(qts, 26)); + g_assert_true(qtest_get_irq(qts, 0)); + + /* + * level trigger wouldn't auto clear clear pending, + * so we need to manually do it. + */ + qtest_writeb(qts, clicintie25_addr, 0); + qtest_writeb(qts, clicintie26_addr, 0); + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 0); + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 26, 0); + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 0, 0); + g_assert_cmpuint(qtest_readb(qts, clicintip25_addr), ==, 0); + g_assert_cmpuint(qtest_readb(qts, clicintip26_addr), ==, 0); + + /* set interrupt 25 level 255, interrupt 26 level 127 */ + /* arbitration will be made and 25 will rise */ + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 1); + g_assert_cmpuint(qtest_readb(qts, clicintip25_addr), ==, 1); + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 26, 1); + g_assert_cmpuint(qtest_readb(qts, clicintip26_addr), ==, 1); + qtest_writeb(qts, clicintctl25_addr, 0xbf); + qtest_writeb(qts, clicintctl26_addr, 0x3f); + qtest_writeb(qts, clicintie25_addr, 1); + g_assert_cmpuint(qtest_readb(qts, clicintie25_addr), ==, 1); + qtest_writeb(qts, clicintie26_addr, 1); + g_assert_cmpuint(qtest_readb(qts, clicintie26_addr), ==, 1); + /* trigger arbitration */ + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 1); + g_assert_true(qtest_irq_delivered(qts, 25)); + g_assert_true(qtest_get_irq(qts, 0)); + + qtest_quit(qts); +} + +/* + * test unvectored negative level triggered interrupt + * test points: + * 1. we use interrupt 25 and 26 to test arbitration in two situation: + * same level, different level. + * 2. within level triggered mode, we can only use device to clear pending. + * 3. within negative level triggered mode, set gpio-in lower + * to trigger interrupt. + */ +static void test_unvectored_negative_level_triggered_interrupt(void) +{ + QTestState *qts = qtest_start(QEMU_BASE_ARGS); + /* intercept in and out irq */ + qtest_irq_intercept_out(qts, "/machine/unattached/device[1]"); + qtest_irq_intercept_in(qts, "/machine/unattached/device[1]"); + + /* configure */ + qtest_writeb(qts, MCLICCFG_ADDR, 0x1); + qtest_writeb(qts, clicintattr25_addr, 0xc4); + qtest_writeb(qts, clicintattr26_addr, 0xc4); + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 1); + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 26, 1); + g_assert_cmpuint(qtest_readb(qts, clicintip25_addr), ==, 0); + g_assert_cmpuint(qtest_readb(qts, clicintip26_addr), ==, 0); + + /* set pending */ + /* arbitration will be made and 26 will be delivered */ + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 0); + g_assert_cmpuint(qtest_readb(qts, clicintip25_addr), ==, 1); + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 26, 0); + g_assert_cmpuint(qtest_readb(qts, clicintip26_addr), ==, 1); + qtest_writeb(qts, clicintie25_addr, 1); + g_assert_cmpuint(qtest_readb(qts, clicintie25_addr), ==, 1); + qtest_writeb(qts, clicintie26_addr, 1); + g_assert_cmpuint(qtest_readb(qts, clicintie26_addr), ==, 1); + /* trigger arbitration */ + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 0); + g_assert_true(qtest_irq_delivered(qts, 26)); + g_assert_true(qtest_get_irq(qts, 0)); + + /* + * level trigger wouldn't auto clear clear pending, + * so we need to manually do it. + */ + qtest_writeb(qts, clicintie25_addr, 0); + qtest_writeb(qts, clicintie26_addr, 0); + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 1); + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 26, 1); + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 0, 0); + g_assert_cmpuint(qtest_readb(qts, clicintip25_addr), ==, 0); + g_assert_cmpuint(qtest_readb(qts, clicintip26_addr), ==, 0); + + /* set interrupt 25 level 255, interrupt 26 level 127 */ + /* arbitration will be made and 25 will rise */ + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 0); + g_assert_cmpuint(qtest_readb(qts, clicintip25_addr), ==, 1); + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 26, 0); + g_assert_cmpuint(qtest_readb(qts, clicintip26_addr), ==, 1); + qtest_writeb(qts, clicintctl25_addr, 0xbf); + qtest_writeb(qts, clicintctl26_addr, 0x3f); + qtest_writeb(qts, clicintie25_addr, 1); + g_assert_cmpuint(qtest_readb(qts, clicintie25_addr), ==, 1); + qtest_writeb(qts, clicintie26_addr, 1); + g_assert_cmpuint(qtest_readb(qts, clicintie26_addr), ==, 1); + /* trigger arbitration */ + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 0); + g_assert_true(qtest_irq_delivered(qts, 25)); + g_assert_true(qtest_get_irq(qts, 0)); + + qtest_quit(qts); +} + +/* + * test unvectored positive edge triggered interrupt + * test points: + * 1. we use interrupt 25 and 26 to test arbitration in same level + * 2. within unvectored edge triggered mode, pending bit can be + * cleared by using nxti instruction which can't be tested in qtest. + * 3. within positive edge triggered mode, set gpio-in + * from lower to rise to trigger interrupt. + */ +static void test_unvectored_positive_edge_triggered_interrupt(void) +{ + QTestState *qts = qtest_start(QEMU_BASE_ARGS); + /* intercept in and out irq */ + qtest_irq_intercept_out(qts, "/machine/unattached/device[1]"); + qtest_irq_intercept_in(qts, "/machine/unattached/device[1]"); + + /* configure */ + qtest_writeb(qts, MCLICCFG_ADDR, 0x1); + qtest_writeb(qts, clicintattr25_addr, 0xc2); + qtest_writeb(qts, clicintattr26_addr, 0xc2); + g_assert_cmpuint(qtest_readb(qts, clicintip25_addr), ==, 0); + g_assert_cmpuint(qtest_readb(qts, clicintip26_addr), ==, 0); + + /* set pending */ + /* arbitration will be made and 26 will be delivered */ + qtest_writeb(qts, clicintip25_addr, 1); + g_assert_cmpuint(qtest_readb(qts, clicintip25_addr), ==, 1); + qtest_writeb(qts, clicintip26_addr, 1); + g_assert_cmpuint(qtest_readb(qts, clicintip26_addr), ==, 1); + qtest_writeb(qts, clicintie25_addr, 1); + g_assert_cmpuint(qtest_readb(qts, clicintie25_addr), ==, 1); + qtest_writeb(qts, clicintie26_addr, 1); + g_assert_cmpuint(qtest_readb(qts, clicintie26_addr), ==, 1); + /* trigger arbitration */ + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 0); + g_assert_true(qtest_irq_delivered(qts, 26)); + g_assert_true(qtest_get_irq(qts, 0)); + + qtest_quit(qts); +} + +/* + * test unvectored negative edge triggered interrupt + * test points: + * 1. we use interrupt 25 and 26 to test arbitration in same level + * 2. within unvectored edge triggered mode, pending bit can be cleared + * by using nxti instruction which can't be tested in qtest. + * 3. within positive edge triggered mode, set gpio-in from + * rise to lower to trigger interrupt. + */ +static void test_unvectored_negative_edge_triggered_interrupt(void) +{ + QTestState *qts = qtest_start(QEMU_BASE_ARGS); + /* intercept in and out irq */ + qtest_irq_intercept_out(qts, "/machine/unattached/device[1]"); + qtest_irq_intercept_in(qts, "/machine/unattached/device[1]"); + + /* configure */ + qtest_writeb(qts, MCLICCFG_ADDR, 0x1); + qtest_writeb(qts, clicintattr25_addr, 0xc6); + qtest_writeb(qts, clicintattr26_addr, 0xc6); + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 1); + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 26, 1); + g_assert_cmpuint(qtest_readb(qts, clicintip25_addr), ==, 0); + g_assert_cmpuint(qtest_readb(qts, clicintip26_addr), ==, 0); + + /* set pending */ + /* arbitration will be made and 26 will be delivered */ + qtest_writeb(qts, clicintip25_addr, 1); + g_assert_cmpuint(qtest_readb(qts, clicintip25_addr), ==, 1); + qtest_writeb(qts, clicintip26_addr, 1); + g_assert_cmpuint(qtest_readb(qts, clicintip26_addr), ==, 1); + qtest_writeb(qts, clicintie25_addr, 1); + g_assert_cmpuint(qtest_readb(qts, clicintie25_addr), ==, 1); + qtest_writeb(qts, clicintie26_addr, 1); + g_assert_cmpuint(qtest_readb(qts, clicintie26_addr), ==, 1); + /* trigger arbitration */ + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 0); + g_assert_true(qtest_irq_delivered(qts, 26)); + g_assert_true(qtest_get_irq(qts, 0)); + + qtest_quit(qts); +} + +/* + * Test that PRV_S is a filtered view of PRV_M. + * + * IRQs configured as PRV_M in the mode field of intattr are not visible via + * the PRV_S registers, and all registers appear as hard-wired zeros. + */ +static void test_prv_s_access(void) +{ + QTestState *qts = qtest_start(QEMU_ARGS(",clic-mode=ms")); + int default_intattr = INTATTR_SHV | (TRIG_LEVEL << INTATTR_TRIG_SHIFT); + int default_reg_value = (default_intattr << INTATTR_SHIFT) | + (PRV_M << REG_MODE_SHIFT) | (0x7 << INTCTL_SHIFT); + int reg_value_2 = (TRIG_FALLING << REG_TRIG_SHIFT) | + (PRV_S << REG_MODE_SHIFT) | (0x5 << INTCTL_SHIFT); + int value; + + /* + * Make sure of our base state using the PRV_M registers + */ + qtest_writel(qts, MCLICCFG_ADDR, TEST_CFG(1)); + /* No PRV_U, so no UNLBITS */ + g_assert_cmpuint(qtest_readl(qts, MCLICCFG_ADDR), == , TEST_CFG_MS(1)); + + qtest_writel(qts, clicint12_addr, default_reg_value); + g_assert_cmpuint(qtest_readl(qts, clicint12_addr), == , default_reg_value); + qtest_writel(qts, clicint25_addr, default_reg_value); + g_assert_cmpuint(qtest_readl(qts, clicint25_addr), == , default_reg_value); + qtest_writel(qts, clicint26_addr, default_reg_value); + g_assert_cmpuint(qtest_readl(qts, clicint26_addr), == , default_reg_value); + + /* + * Now check the PRV_S view + */ + + /* We should only see the PRV_S part of CLICCFG */ + g_assert_cmpuint(qtest_readl(qts, SCLICCFG_ADDR), == , TEST_CFG_S(1)); + + /* These are all PRV_M mode so reading via PRV_S should see them all as 0 */ + g_assert_cmpuint(qtest_readl(qts, clicint12_addr_s), == , 0); + g_assert_cmpuint(qtest_readl(qts, clicint25_addr_s), == , 0); + g_assert_cmpuint(qtest_readl(qts, clicint26_addr_s), == , 0); + + /* Writing should leave them unchanged */ + qtest_writel(qts, clicint12_addr_s, 0x55555555); + g_assert_cmpuint(qtest_readl(qts, clicint12_addr), == , default_reg_value); + qtest_writel(qts, clicint25_addr_s, 0x55555555); + g_assert_cmpuint(qtest_readl(qts, clicint25_addr), == , default_reg_value); + qtest_writel(qts, clicint26_addr_s, 0x55555555); + g_assert_cmpuint(qtest_readl(qts, clicint26_addr), == , default_reg_value); + + /* + * If we change IRQ 12 to PRV_S mode, we should now be able to see it + */ + value = (default_reg_value & ~REG_MODE_MASK) | (PRV_S << REG_MODE_SHIFT); + qtest_writel(qts, clicint12_addr, value); + g_assert_cmpuint(qtest_readl(qts, clicint12_addr_s), == , value); + + /* ...but not the others */ + g_assert_cmpuint(qtest_readl(qts, clicint25_addr_s), == , 0); + g_assert_cmpuint(qtest_readl(qts, clicint26_addr_s), == , 0); + + /* We should also be able to write to it */ + qtest_writel(qts, clicint12_addr_s, reg_value_2); + g_assert_cmpuint(qtest_readl(qts, clicint12_addr), == , reg_value_2); + g_assert_cmpuint(qtest_readl(qts, clicint12_addr_s), == , reg_value_2); + + /* ...but not the others */ + qtest_writel(qts, clicint25_addr_s, 0x55555555); + g_assert_cmpuint(qtest_readl(qts, clicint25_addr), == , default_reg_value); + qtest_writel(qts, clicint26_addr_s, 0x55555555); + g_assert_cmpuint(qtest_readl(qts, clicint26_addr), == , default_reg_value); + + qtest_quit(qts); +} + +/* + * Test that PRV_U is a filtered view of PRV_M. + * + * IRQs configured as PRV_M in the mode field of intattr are not visible via + * the PRV_U registers, and all registers appear as hard-wired zeros. + */ +static void test_prv_u_access(void) +{ + QTestState *qts = qtest_start(QEMU_ARGS(",clic-mode=mu")); + int default_intattr = INTATTR_SHV | (TRIG_LEVEL << INTATTR_TRIG_SHIFT); + int default_reg_value = (default_intattr << INTATTR_SHIFT) | + (PRV_M << REG_MODE_SHIFT) | (0x7 << INTCTL_SHIFT); + int reg_value_2 = (TRIG_FALLING << REG_TRIG_SHIFT) | + (PRV_U << REG_MODE_SHIFT) | (0x5 << INTCTL_SHIFT); + int value; + + /* + * Make sure of our base state using the PRV_M registers + */ + qtest_writel(qts, MCLICCFG_ADDR, TEST_CFG(1)); + /* No PRV_S, so no SNLBITS */ + g_assert_cmpuint(qtest_readl(qts, MCLICCFG_ADDR), == , TEST_CFG_MU(1)); + + qtest_writel(qts, clicint12_addr, default_reg_value); + g_assert_cmpuint(qtest_readl(qts, clicint12_addr), == , default_reg_value); + qtest_writel(qts, clicint25_addr, default_reg_value); + g_assert_cmpuint(qtest_readl(qts, clicint25_addr), == , default_reg_value); + qtest_writel(qts, clicint26_addr, default_reg_value); + g_assert_cmpuint(qtest_readl(qts, clicint26_addr), == , default_reg_value); + + /* + * Now check the PRV_U view. Note we only have one additional mode, so we + * use the xxx_addr_s register bank. + */ + + /* We should only see the PRV_U part of CLICCFG */ + g_assert_cmpuint(qtest_readl(qts, SCLICCFG_ADDR), == , TEST_CFG_U(1)); + + /* These are all PRV_M mode so reading via PRV_S should see them all as 0 */ + g_assert_cmpuint(qtest_readl(qts, clicint12_addr_s), == , 0); + g_assert_cmpuint(qtest_readl(qts, clicint25_addr_s), == , 0); + g_assert_cmpuint(qtest_readl(qts, clicint26_addr_s), == , 0); + + /* Writing should leave them unchanged */ + qtest_writel(qts, clicint12_addr_s, 0x55555555); + g_assert_cmpuint(qtest_readl(qts, clicint12_addr), == , default_reg_value); + qtest_writel(qts, clicint25_addr_s, 0x55555555); + g_assert_cmpuint(qtest_readl(qts, clicint25_addr), == , default_reg_value); + qtest_writel(qts, clicint26_addr_s, 0x55555555); + g_assert_cmpuint(qtest_readl(qts, clicint26_addr), == , default_reg_value); + + /* + * If we change IRQ 12 to PRV_U mode, we should now be able to see it + */ + value = (default_reg_value & ~REG_MODE_MASK) | (PRV_U << REG_MODE_SHIFT); + qtest_writel(qts, clicint12_addr, value); + g_assert_cmpuint(qtest_readl(qts, clicint12_addr_s), == , value); + + /* ...but not the others */ + g_assert_cmpuint(qtest_readl(qts, clicint25_addr_s), == , 0); + g_assert_cmpuint(qtest_readl(qts, clicint26_addr_s), == , 0); + + /* We should also be able to write to it */ + qtest_writel(qts, clicint12_addr_s, reg_value_2); + g_assert_cmpuint(qtest_readl(qts, clicint12_addr), == , reg_value_2); + g_assert_cmpuint(qtest_readl(qts, clicint12_addr_s), == , reg_value_2); + + /* ...but not the others */ + qtest_writel(qts, clicint25_addr_s, 0x55555555); + g_assert_cmpuint(qtest_readl(qts, clicint25_addr), == , default_reg_value); + qtest_writel(qts, clicint26_addr_s, 0x55555555); + g_assert_cmpuint(qtest_readl(qts, clicint26_addr), == , default_reg_value); + + qtest_quit(qts); +} + +/* + * Test that PRV_S and PRV_U are filtered views of PRV_M. + * + * IRQs configured as PRV_M in the mode field of intattr are not visible via + * the PRV_S or PRV_U registers, and all registers appear as hard-wired zeros. + */ +static void test_prv_su_access(void) +{ + QTestState *qts = qtest_start(QEMU_ARGS(",clic-mode=msu")); + int default_intattr = INTATTR_SHV | (TRIG_LEVEL << INTATTR_TRIG_SHIFT); + int default_reg_value = (default_intattr << INTATTR_SHIFT) | + (PRV_M << REG_MODE_SHIFT) | (0x7 << INTCTL_SHIFT); + int reg_value_2 = (TRIG_FALLING << REG_TRIG_SHIFT) | + (PRV_S << REG_MODE_SHIFT) | (0x5 << INTCTL_SHIFT); + int reg_value_3 = (TRIG_RISING << REG_TRIG_SHIFT) | + (PRV_U << REG_MODE_SHIFT) | (0x2 << INTCTL_SHIFT); + int reg_value_4 = INTATTR_SHV | (TRIG_HIGH << REG_TRIG_SHIFT) | + (PRV_U << REG_MODE_SHIFT) | (0x3 << INTCTL_SHIFT); + int value; + + /* + * Make sure of our base state using the PRV_M registers + */ + qtest_writel(qts, MCLICCFG_ADDR, TEST_CFG(2)); + g_assert_cmpuint(qtest_readl(qts, MCLICCFG_ADDR), == , TEST_CFG_MSU(2)); + + qtest_writel(qts, clicint12_addr, default_reg_value); + g_assert_cmpuint(qtest_readl(qts, clicint12_addr), == , default_reg_value); + qtest_writel(qts, clicint25_addr, default_reg_value); + g_assert_cmpuint(qtest_readl(qts, clicint25_addr), == , default_reg_value); + qtest_writel(qts, clicint26_addr, default_reg_value); + g_assert_cmpuint(qtest_readl(qts, clicint26_addr), == , default_reg_value); + + /* + * Now check the PRV_S view + */ + + /* We should only see the PRV_S and PRV_U parts of CLICCFG */ + g_assert_cmpuint(qtest_readl(qts, SCLICCFG_ADDR), == , TEST_CFG_SU(2)); + + /* These are all PRV_M mode so reading via PRV_S should see them all as 0 */ + g_assert_cmpuint(qtest_readl(qts, clicint12_addr_s), == , 0); + g_assert_cmpuint(qtest_readl(qts, clicint25_addr_s), == , 0); + g_assert_cmpuint(qtest_readl(qts, clicint26_addr_s), == , 0); + + /* Writing should leave them unchanged */ + qtest_writel(qts, clicint12_addr_s, 0x55555555); + g_assert_cmpuint(qtest_readl(qts, clicint12_addr), == , default_reg_value); + qtest_writel(qts, clicint25_addr_s, 0x55555555); + g_assert_cmpuint(qtest_readl(qts, clicint25_addr), == , default_reg_value); + qtest_writel(qts, clicint26_addr_s, 0x55555555); + g_assert_cmpuint(qtest_readl(qts, clicint26_addr), == , default_reg_value); + + /* + * If we change IRQ 12 to PRV_S mode, we should now be able to see it + */ + value = (default_reg_value & ~REG_MODE_MASK) | (PRV_S << REG_MODE_SHIFT); + qtest_writel(qts, clicint12_addr, value); + g_assert_cmpuint(qtest_readl(qts, clicint12_addr_s), == , value); + + /* ...but not the others */ + g_assert_cmpuint(qtest_readl(qts, clicint25_addr_s), == , 0); + g_assert_cmpuint(qtest_readl(qts, clicint26_addr_s), == , 0); + + /* We should also be able to write to it */ + qtest_writel(qts, clicint12_addr_s, reg_value_2); + g_assert_cmpuint(qtest_readl(qts, clicint12_addr), == , reg_value_2); + g_assert_cmpuint(qtest_readl(qts, clicint12_addr_s), == , reg_value_2); + + /* ...but not the others */ + qtest_writel(qts, clicint25_addr_s, 0x55555555); + g_assert_cmpuint(qtest_readl(qts, clicint25_addr), == , default_reg_value); + qtest_writel(qts, clicint26_addr_s, 0x55555555); + g_assert_cmpuint(qtest_readl(qts, clicint26_addr), == , default_reg_value); + + /* + * Now check the PRV_U view + */ + + /* We should only see the PRV_U part of CLICCFG */ + g_assert_cmpuint(qtest_readl(qts, UCLICCFG_ADDR), == , TEST_CFG_U(2)); + + /* These are all PRV_M or PRV_S so reading via PRV_U should see them as 0 */ + g_assert_cmpuint(qtest_readl(qts, clicint12_addr_u), == , 0); + g_assert_cmpuint(qtest_readl(qts, clicint25_addr_u), == , 0); + g_assert_cmpuint(qtest_readl(qts, clicint26_addr_u), == , 0); + + /* Writing should leave them unchanged */ + qtest_writel(qts, clicint12_addr_u, 0x55555555); + g_assert_cmpuint(qtest_readl(qts, clicint12_addr), == , reg_value_2); + qtest_writel(qts, clicint25_addr_u, 0x55555555); + g_assert_cmpuint(qtest_readl(qts, clicint25_addr), == , default_reg_value); + qtest_writel(qts, clicint26_addr_u, 0x55555555); + g_assert_cmpuint(qtest_readl(qts, clicint26_addr), == , default_reg_value); + + /* + * If we change IRQ 25 to PRV_U mode, we should now be able to see it + * in both PRV_S and PRV_U modes + */ + value = (default_reg_value & ~REG_MODE_MASK) | (PRV_U << REG_MODE_SHIFT); + qtest_writel(qts, clicint25_addr, value); + g_assert_cmpuint(qtest_readl(qts, clicint25_addr_s), == , value); + g_assert_cmpuint(qtest_readl(qts, clicint25_addr_u), == , value); + + /* ...we can't see the others in PRV_U */ + g_assert_cmpuint(qtest_readl(qts, clicint12_addr_u), == , 0); + g_assert_cmpuint(qtest_readl(qts, clicint26_addr_u), == , 0); + + /* We should also be able to write to it from both PRV_S and PRV_U */ + qtest_writel(qts, clicint25_addr_s, reg_value_3); + g_assert_cmpuint(qtest_readl(qts, clicint25_addr), == , reg_value_3); + g_assert_cmpuint(qtest_readl(qts, clicint25_addr_s), == , reg_value_3); + g_assert_cmpuint(qtest_readl(qts, clicint25_addr_u), == , reg_value_3); + qtest_writel(qts, clicint25_addr_u, reg_value_4); + g_assert_cmpuint(qtest_readl(qts, clicint25_addr), == , reg_value_4); + g_assert_cmpuint(qtest_readl(qts, clicint25_addr_s), == , reg_value_4); + g_assert_cmpuint(qtest_readl(qts, clicint25_addr_u), == , reg_value_4); + + /* ...but we still can't write to the others in PRV_U */ + qtest_writel(qts, clicint12_addr_u, 0x55555555); + g_assert_cmpuint(qtest_readl(qts, clicint12_addr), == , reg_value_2); + qtest_writel(qts, clicint26_addr_u, 0x55555555); + g_assert_cmpuint(qtest_readl(qts, clicint26_addr), == , default_reg_value); + + qtest_quit(qts); +} + +/* Test configuration in PRV_M-only mode */ +static void clic_configure_reg_mmio_test_case_m(void) +{ + /* Start QEMU */ + qtest_add_func("virt/clic/prv_m/boot_qemu_m", + boot_qemu_m); + + /* cliccfg configure case */ + qtest_add_func("virt/clic/prv_m/cliccfg_min_mnlbits", + test_configure_cliccfg_min_mnlbits); + qtest_add_func("virt/clic/prv_m/cliccfg_supported_max_mnlbits", + test_configure_cliccfg_supported_max_mnlbits); + qtest_add_func("virt/clic/prv_m/cliccfg_unsupported_mnlbits", + test_configure_cliccfg_unsupported_mnlbits); + /* snlbits and unlbits should not work */ + qtest_add_func("virt/clic/prv_m/cliccfg_snlbits_no_s", + test_configure_cliccfg_snlbits_no_s); + qtest_add_func("virt/clic/prv_m/cliccfg_snlbits_no_u", + test_configure_cliccfg_unlbits_no_u); + /* clicintip configure case */ + qtest_add_func("virt/clic/prv_m/clicintip_level_triggered_readonly", + test_configure_clicintip_level_triggered_read_only); + + /* clicintie configure case */ + qtest_add_func("virt/clic/prv_m/clicintie_enable", + test_configure_clicintie_enable); + qtest_add_func("virt/clic/prv_m/clicintie_disable", + test_configure_clicintie_disable); + + /* clicintattr mode configure cases are all PRV_M WARL - nmbits == 0*/ + qtest_add_func("virt/clic/prv_m/cliccfg_nmbits_0_m", + test_configure_cliccfg_nmbits_0); + qtest_add_func("virt/clic/prv_m/intattr_prv_m", + test_configure_clicintattr_prv_m); + qtest_add_func("virt/clic/prv_m/intattr_prv_s_to_m_warl", + test_configure_clicintattr_prv_s_to_m_warl); + qtest_add_func("virt/clic/prv_m/intattr_prv_u_to_m_warl", + test_configure_clicintattr_prv_u_to_m_warl); + qtest_add_func("virt/clic/prv_m/intattr_unsupported_mode_10", + test_configure_clicintattr_unsupported_mode_10); + + /* unsupported nmbits */ + qtest_add_func("virt/clic/prv_m/cliccfg_unsupported_nmbits_1_m", + test_configure_cliccfg_unsupported_nmbits_1); + qtest_add_func("virt/clic/prv_m/cliccfg_unsupported_nmbits_2_m", + test_configure_cliccfg_unsupported_nmbits_2); + qtest_add_func("virt/clic/prv_m/cliccfg_unsupported_nmbits_3_m", + test_configure_cliccfg_unsupported_nmbits_3); + + /* clicintattr TRIG and SHV */ + qtest_add_func("virt/clic/prv_m/intattr_positive_edge_triggered", + test_configure_clicintattr_positive_edge_triggered); + qtest_add_func("virt/clic/prv_m/clicintattr_negative_edge_triggered", + test_configure_clicintattr_negative_edge_triggered); + qtest_add_func("virt/clic/prv_m/clicintattr_positive_level_triggered", + test_configure_clicintattr_positive_level_triggered); + qtest_add_func("virt/clic/prv_m/clicintattr_negative_level_triggered", + test_configure_clicintattr_negative_level_triggered); + qtest_add_func("virt/clic/prv_m/clicintattr_non_vectored", + test_configure_clicintattr_non_vectored); + + /* Shut down QEMU */ + qtest_add_func("virt/clic/prv_m/shut_down_qemu_m", + shut_down_qemu); +} + +/* Test configuration in PRV_M + PRV_S mode */ +static void clic_configure_reg_mmio_test_case_ms(void) +{ + /* Start QEMU */ + qtest_add_func("virt/clic/prv_ms/boot_qemu_ms", + boot_qemu_ms); + + /* cliccfg configure cases */ + + /* mnlbits should be unaffected */ + qtest_add_func("virt/clic/prv_ms/cliccfg_min_mnlbits_ms", + test_configure_cliccfg_min_mnlbits); + qtest_add_func("virt/clic/prv_ms/cliccfg_supported_max_mnlbits_ms", + test_configure_cliccfg_supported_max_mnlbits); + qtest_add_func("virt/clic/prv_ms/cliccfg_unsupported_mnlbits_ms", + test_configure_cliccfg_unsupported_mnlbits); + /* snlbits should work*/ + qtest_add_func("virt/clic/prv_ms/cliccfg_min_snlbits_s_ms", + test_configure_cliccfg_min_snlbits_s); + qtest_add_func("virt/clic/prv_ms/cliccfg_supported_max_snlbits_s_ms", + test_configure_cliccfg_supported_max_snlbits_s); + qtest_add_func("virt/clic/prv_ms/cliccfg_unsupported_snlbits_s_ms", + test_configure_cliccfg_unsupported_snlbits_s); + /* unlbits should not work */ + qtest_add_func("virt/clic/prv_ms/cliccfg_unlbits_no_u_ms", + test_configure_cliccfg_unlbits_no_u); + + /* clicintattr mode configure cases with nmbits = 1 */ + qtest_add_func("virt/clic/prv_ms/cliccfg_nmbits_1_ms", + test_configure_cliccfg_nmbits_1); + qtest_add_func("virt/clic/prv_ms/intattr_prv_m_nmbits_1", + test_configure_clicintattr_prv_m); + qtest_add_func("virt/clic/prv_ms/intattr_unsupported_mode_10_nmbits_1", + test_configure_clicintattr_unsupported_mode_10); + qtest_add_func("virt/clic/prv_ms/intattr_prv_u_to_s_warl_nmbits_1_prv_m", + test_configure_clicintattr_prv_u_to_s_warl); + qtest_add_func("virt/clic/prv_ms/intattr_prv_s_supported_nmbits_1", + test_configure_clicintattr_prv_s_supported); + qtest_add_func("virt/clic/prv_ms/intattr_prv_u_to_s_warl_nmbits_1_prv_s", + test_configure_clicintattr_prv_u_to_s_warl); + + /* clicintattr mode configure cases with nmbits = 0 - PRV_M only */ + qtest_add_func("virt/clic/prv_ms/cliccfg_nmbits_0_ms", + test_configure_cliccfg_nmbits_0); + qtest_add_func("virt/clic/prv_ms/intattr_prv_m_nmbits_0", + test_configure_clicintattr_prv_m); + qtest_add_func("virt/clic/prv_ms/intattr_unsupported_mode_10_nmbits_0", + test_configure_clicintattr_unsupported_mode_10); + qtest_add_func("virt/clic/prv_ms/intattr_prv_u_to_m_warl_nmbits_0", + test_configure_clicintattr_prv_u_to_m_warl); + qtest_add_func("virt/clic/prv_ms/intattr_prv_s_to_m_warl_nmbits_0", + test_configure_clicintattr_prv_s_to_m_warl); + + /* unsupported nmbits */ + qtest_add_func("virt/clic/prv_ms/cliccfg_unsupported_nmbits_2_ms", + test_configure_cliccfg_unsupported_nmbits_2); + qtest_add_func("virt/clic/prv_ms/cliccfg_unsupported_nmbits_3_ms", + test_configure_cliccfg_unsupported_nmbits_3); + + /* clicintattr TRIG and SHV */ + qtest_add_func("virt/clic/prv_ms/intattr_positive_edge_triggered", + test_configure_clicintattr_positive_edge_triggered); + qtest_add_func("virt/clic/prv_ms/clicintattr_negative_edge_triggered", + test_configure_clicintattr_negative_edge_triggered); + qtest_add_func("virt/clic/prv_ms/clicintattr_positive_level_triggered", + test_configure_clicintattr_positive_level_triggered); + qtest_add_func("virt/clic/prv_ms/clicintattr_negative_level_triggered", + test_configure_clicintattr_negative_level_triggered); + qtest_add_func("virt/clic/prv_ms/clicintattr_non_vectored", + test_configure_clicintattr_non_vectored); + + /* Shut down QEMU */ + qtest_add_func("virt/clic/prv_ms/shut_down_qemu_ms", + shut_down_qemu); +} + +/* Test configuration in PRV_M + PRV_U mode */ +static void clic_configure_reg_mmio_test_case_mu(void) +{ + /* Start QEMU */ + qtest_add_func("virt/clic/prv_mu/boot_qemu_mu", + boot_qemu_mu); + + /* cliccfg configure cases */ + + /* mnlbits should be unaffected */ + qtest_add_func("virt/clic/prv_mu/cliccfg_min_mnlbits_mu", + test_configure_cliccfg_min_mnlbits); + qtest_add_func("virt/clic/prv_mu/cliccfg_supported_max_mnlbits_mu", + test_configure_cliccfg_supported_max_mnlbits); + qtest_add_func("virt/clic/prv_mu/cliccfg_unsupported_mnlbits_mu", + test_configure_cliccfg_unsupported_mnlbits); + /* snlbits should not work */ + qtest_add_func("virt/clic/prv_mu/cliccfg_snlbits_no_s_mu", + test_configure_cliccfg_snlbits_no_s); + /* unlbits should work*/ + qtest_add_func("virt/clic/prv_mu/cliccfg_min_unlbits_u_mu", + test_configure_cliccfg_min_unlbits_u); + qtest_add_func("virt/clic/prv_mu/cliccfg_uupported_max_unlbits_u_mu", + test_configure_cliccfg_supported_max_unlbits_u); + qtest_add_func("virt/clic/prv_mu/cliccfg_unsupported_unlbits_u_mu", + test_configure_cliccfg_unsupported_unlbits_u); + + /* clicintattr mode configure cases with nmbits = 1 */ + qtest_add_func("virt/clic/prv_mu/cliccfg_nmbits_1_ms", + test_configure_cliccfg_nmbits_1); + qtest_add_func("virt/clic/prv_mu/intattr_prv_m_nmbits_1", + test_configure_clicintattr_prv_m); + qtest_add_func("virt/clic/prv_mu/intattr_unsupported_mode_10_nmbits_1", + test_configure_clicintattr_unsupported_mode_10); + qtest_add_func("virt/clic/prv_mu/intattr_prv_s_to_u_warl_nmbits_1_prv_m", + test_configure_clicintattr_prv_s_to_u_warl); + qtest_add_func("virt/clic/prv_mu/intattr_prv_u_supported_nmbits_1", + test_configure_clicintattr_prv_u_supported); + qtest_add_func("virt/clic/prv_mu/intattr_prv_s_to_u_warl_nmbits_1_prv_u", + test_configure_clicintattr_prv_s_to_u_warl); + + /* clicintattr mode configure cases with nmbits = 0 - PRV_M only */ + qtest_add_func("virt/clic/prv_mu/cliccfg_nmbits_0_ms", + test_configure_cliccfg_nmbits_0); + qtest_add_func("virt/clic/prv_mu/intattr_prv_m_nmbits_0", + test_configure_clicintattr_prv_m); + qtest_add_func("virt/clic/prv_mu/intattr_unsupported_mode_10_nmbits_0", + test_configure_clicintattr_unsupported_mode_10); + qtest_add_func("virt/clic/prv_mu/intattr_prv_u_to_m_warl_nmbits_0", + test_configure_clicintattr_prv_u_to_m_warl); + qtest_add_func("virt/clic/prv_mu/intattr_prv_s_to_m_warl_nmbits_0", + test_configure_clicintattr_prv_s_to_m_warl); + + /* unsupported nmbits */ + qtest_add_func("virt/clic/prv_mu/cliccfg_unsupported_nmbits_2_ms", + test_configure_cliccfg_unsupported_nmbits_2); + qtest_add_func("virt/clic/prv_mu/cliccfg_unsupported_nmbits_3_ms", + test_configure_cliccfg_unsupported_nmbits_3); + + /* clicintattr TRIG and SHV */ + qtest_add_func("virt/clic/prv_mu/intattr_positive_edge_triggered", + test_configure_clicintattr_positive_edge_triggered); + qtest_add_func("virt/clic/prv_mu/clicintattr_negative_edge_triggered", + test_configure_clicintattr_negative_edge_triggered); + qtest_add_func("virt/clic/prv_mu/clicintattr_positive_level_triggered", + test_configure_clicintattr_positive_level_triggered); + qtest_add_func("virt/clic/prv_mu/clicintattr_negative_level_triggered", + test_configure_clicintattr_negative_level_triggered); + qtest_add_func("virt/clic/prv_mu/clicintattr_non_vectored", + test_configure_clicintattr_non_vectored); + + /* Shut down QEMU */ + qtest_add_func("virt/clic/prv_mu/shut_down_qemu_mu", + shut_down_qemu); +} + +/* Test configuration in PRV_M + PRV_S + PRV_U mode */ +static void clic_configure_reg_mmio_test_case_msu(void) +{ + /* Start QEMU */ + qtest_add_func("virt/clic/prv_msu/boot_qemu_msu", + boot_qemu_msu); + + /* cliccfg configure cases */ + + /* mnlbits should be unaffected */ + qtest_add_func("virt/clic/prv_msu/cliccfg_min_mnlbits_msu", + test_configure_cliccfg_min_mnlbits); + qtest_add_func("virt/clic/prv_msu/cliccfg_supported_max_mnlbits_msu", + test_configure_cliccfg_supported_max_mnlbits); + qtest_add_func("virt/clic/prv_msu/cliccfg_unsupported_mnlbits_msu", + test_configure_cliccfg_unsupported_mnlbits); + /* snlbits should work*/ + qtest_add_func("virt/clic/prv_msu/cliccfg_min_snlbits_s_msu", + test_configure_cliccfg_min_snlbits_s); + qtest_add_func("virt/clic/prv_msu/cliccfg_supported_max_snlbits_s_msu", + test_configure_cliccfg_supported_max_snlbits_s); + qtest_add_func("virt/clic/prv_msu/cliccfg_unsupported_snlbits_s_msu", + test_configure_cliccfg_unsupported_snlbits_s); + /* unlbits should work*/ + qtest_add_func("virt/clic/prv_msu/cliccfg_min_unlbits_u_msu", + test_configure_cliccfg_min_unlbits_u); + qtest_add_func("virt/clic/prv_msu/cliccfg_uupported_max_unlbits_u_msu", + test_configure_cliccfg_supported_max_unlbits_u); + qtest_add_func("virt/clic/prv_msu/cliccfg_unsupported_unlbits_u_msu", + test_configure_cliccfg_unsupported_unlbits_u); + /* all bits should work */ + qtest_add_func("virt/clic/prv_msu/cliccfg_xnlbits_msu", + test_configure_cliccfg_xnlbits); + + /* clicintattr mode configure cases with nmbits = 2 => all modes */ + qtest_add_func("virt/clic/prv_msu/cliccfg_nmbits_2_ms", + test_configure_cliccfg_nmbits_2); + qtest_add_func("virt/clic/prv_msu/intattr_prv_m_nmbits_2", + test_configure_clicintattr_prv_m); + qtest_add_func("virt/clic/prv_msu/intattr_unsupported_mode_10_nmbits_2", + test_configure_clicintattr_unsupported_mode_10); + qtest_add_func("virt/clic/prv_msu/intattr_prv_s_supported_nmbits_2", + test_configure_clicintattr_prv_s_supported); + qtest_add_func("virt/clic/prv_msu/intattr_prv_u_supported_nmbits_2", + test_configure_clicintattr_prv_u_supported); + + /* clicintattr mode configure cases with nmbits = 1 => PRV_M and PRV_S */ + qtest_add_func("virt/clic/prv_msu/cliccfg_nmbits_1_ms", + test_configure_cliccfg_nmbits_1); + qtest_add_func("virt/clic/prv_msu/intattr_prv_m_nmbits_1", + test_configure_clicintattr_prv_m); + qtest_add_func("virt/clic/prv_msu/intattr_unsupported_mode_10_nmbits_1", + test_configure_clicintattr_unsupported_mode_10); + qtest_add_func("virt/clic/prv_msu/intattr_prv_u_to_s_warl_nmbits_1_prv_m", + test_configure_clicintattr_prv_u_to_s_warl); + qtest_add_func("virt/clic/prv_msu/intattr_prv_s_supported_nmbits_1", + test_configure_clicintattr_prv_s_supported); + qtest_add_func("virt/clic/prv_msu/intattr_prv_u_to_s_warl_nmbits_1_prv_s", + test_configure_clicintattr_prv_u_to_s_warl); + + /* clicintattr mode configure cases with nmbits = 0 - PRV_M only */ + qtest_add_func("virt/clic/prv_msu/cliccfg_nmbits_0_ms", + test_configure_cliccfg_nmbits_0); + qtest_add_func("virt/clic/prv_msu/intattr_prv_m_nmbits_0", + test_configure_clicintattr_prv_m); + qtest_add_func("virt/clic/prv_msu/intattr_unsupported_mode_10_nmbits_0", + test_configure_clicintattr_unsupported_mode_10); + qtest_add_func("virt/clic/prv_msu/intattr_prv_u_to_m_warl_nmbits_0", + test_configure_clicintattr_prv_u_to_m_warl); + qtest_add_func("virt/clic/prv_msu/intattr_prv_s_to_m_warl_nmbits_0", + test_configure_clicintattr_prv_s_to_m_warl); + + /* unsupported nmbits */ + qtest_add_func("virt/clic/prv_msu/cliccfg_unsupported_nmbits_3_ms", + test_configure_cliccfg_unsupported_nmbits_3); + + /* clicintattr TRIG and SHV */ + qtest_add_func("virt/clic/prv_msu/intattr_positive_edge_triggered", + test_configure_clicintattr_positive_edge_triggered); + qtest_add_func("virt/clic/prv_msu/clicintattr_negative_edge_triggered", + test_configure_clicintattr_negative_edge_triggered); + qtest_add_func("virt/clic/prv_msu/clicintattr_positive_level_triggered", + test_configure_clicintattr_positive_level_triggered); + qtest_add_func("virt/clic/prv_msu/clicintattr_negative_level_triggered", + test_configure_clicintattr_negative_level_triggered); + qtest_add_func("virt/clic/prv_msu/clicintattr_non_vectored", + test_configure_clicintattr_non_vectored); + + /* Shut down QEMU */ + qtest_add_func("virt/clic/prv_msu/shut_down_qemu_msu", + shut_down_qemu); +} + +#define GEN_INTCTL_TEST(_nbits) \ +GEN_BOOT_QEMU_INTCTL(_nbits) \ +static void clic_configure_clicintctl_test_case_##_nbits##_bits(void) \ +{ \ + g_autofree char *prefix = g_strdup_printf("virt/clic/clicintl_%d_bits", \ + _nbits); \ + g_autofree char *path; \ + \ + path = g_strdup_printf("%s/boot_qemu", prefix); \ + qtest_add_func(path, boot_qemu_##_nbits##_bits); \ + \ + path = g_strdup_printf("%s/intctl_0_%d_bits", prefix, _nbits); \ + qtest_add_func(path, \ + test_configure_clicintctl_set_0_##_nbits##_bits); \ + path = g_strdup_printf("%s/intctl_33_%d_bits", prefix, _nbits); \ + qtest_add_func(path, \ + test_configure_clicintctl_set_33_##_nbits##_bits); \ + path = g_strdup_printf("%s/intctl_88_%d_bits", prefix, _nbits); \ + qtest_add_func(path, \ + test_configure_clicintctl_set_88_##_nbits##_bits); \ + path = g_strdup_printf("%s/intctl_128_%d_bits", prefix, _nbits); \ + qtest_add_func(path, \ + test_configure_clicintctl_set_128_##_nbits##_bits); \ + path = g_strdup_printf("%s/intctl_204_%d_bits", prefix, _nbits); \ + qtest_add_func(path, \ + test_configure_clicintctl_set_204_##_nbits##_bits); \ + path = g_strdup_printf("%s/intctl_240_%d_bits", prefix, _nbits); \ + qtest_add_func(path, \ + test_configure_clicintctl_set_240_##_nbits##_bits); \ + \ + path = g_strdup_printf("%s/shut_down_qemu", prefix); \ + qtest_add_func(path, shut_down_qemu); \ +} + +GEN_INTCTL_TEST(0) +GEN_INTCTL_TEST(1) +GEN_INTCTL_TEST(2) +GEN_INTCTL_TEST(3) +GEN_INTCTL_TEST(4) +GEN_INTCTL_TEST(5) +GEN_INTCTL_TEST(6) +GEN_INTCTL_TEST(7) +GEN_INTCTL_TEST(8) + +static void clic_irq_test_case(void) +{ + /* interrupt test case */ + qtest_add_func("virt/clic/vectored_positive_level_triggered_interrupt", + test_vectored_positive_level_triggered_interrupt); + qtest_add_func("virt/clic/vectored_negative_level_triggered_interrupt", + test_vectored_negative_level_triggered_interrupt); + qtest_add_func("virt/clic/vectored_positive_edge_triggered_interrupt", + test_vectored_positive_edge_triggered_interrupt); + qtest_add_func("virt/clic/vectored_negative_edge_triggered_interrupt", + test_vectored_negative_edge_triggered_interrupt); + qtest_add_func("virt/clic/unvectored_positive_level_triggered_interrupt", + test_unvectored_positive_level_triggered_interrupt); + qtest_add_func("virt/clic/unvectored_negative_level_triggered_interrupt", + test_unvectored_negative_level_triggered_interrupt); + qtest_add_func("virt/clic/unvectored_positive_edge_triggered_interrupt", + test_unvectored_positive_edge_triggered_interrupt); + qtest_add_func("virt/clic/unvectored_negative_edge_triggered_interrupt", + test_unvectored_negative_edge_triggered_interrupt); +} + +static void clic_mode_access_test_case(void) +{ + qtest_add_func("virt/clic/test_prv_s_access", test_prv_s_access); + qtest_add_func("virt/clic/test_prv_u_access", test_prv_u_access); + qtest_add_func("virt/clic/test_prv_su_access", test_prv_su_access); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + g_test_set_nonfatal_assertions(); + + /* test cases */ + clic_configure_reg_mmio_test_case_m(); + clic_configure_reg_mmio_test_case_ms(); + clic_configure_reg_mmio_test_case_mu(); + clic_configure_reg_mmio_test_case_msu(); + clic_configure_clicintctl_test_case_0_bits(); + clic_configure_clicintctl_test_case_1_bits(); + clic_configure_clicintctl_test_case_2_bits(); + clic_configure_clicintctl_test_case_3_bits(); + clic_configure_clicintctl_test_case_4_bits(); + clic_configure_clicintctl_test_case_5_bits(); + clic_configure_clicintctl_test_case_6_bits(); + clic_configure_clicintctl_test_case_7_bits(); + clic_configure_clicintctl_test_case_8_bits(); + clic_irq_test_case(); + clic_mode_access_test_case(); + + /* Run the tests */ + int ret = g_test_run(); + + return ret; +}