From patchwork Fri Mar 31 01:01:16 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Joe Komlodi X-Patchwork-Id: 1763547 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=nongnu.org (client-ip=209.51.188.17; helo=lists.gnu.org; envelope-from=qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org; receiver=) Authentication-Results: legolas.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=google.com header.i=@google.com header.a=rsa-sha256 header.s=20210112 header.b=TkdWcjl1; dkim-atps=neutral Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-ECDSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4Pnhp748Xgz1yYr for ; Fri, 31 Mar 2023 12:02:06 +1100 (AEDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1pi39C-0003B8-Lb; Thu, 30 Mar 2023 21:01:42 -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 <37zAmZAcKChI26436v0y66y3w.u648w4C-vwDw3565y5C.69y@flex--komlodi.bounces.google.com>) id 1pi39A-0003Ag-PO for qemu-devel@nongnu.org; Thu, 30 Mar 2023 21:01:40 -0400 Received: from mail-yb1-xb4a.google.com ([2607:f8b0:4864:20::b4a]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from <37zAmZAcKChI26436v0y66y3w.u648w4C-vwDw3565y5C.69y@flex--komlodi.bounces.google.com>) id 1pi398-0006Zv-5n for qemu-devel@nongnu.org; Thu, 30 Mar 2023 21:01:40 -0400 Received: by mail-yb1-xb4a.google.com with SMTP id z31-20020a25a122000000b00b38d2b9a2e9so20306310ybh.3 for ; Thu, 30 Mar 2023 18:01:36 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20210112; t=1680224495; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=FwjqfUNaxStAr97zk6xPM5MCnKIclcyU4ODUMMQVp9o=; b=TkdWcjl1J+Yhutu9B4aGOjpSgKqgq1Dxk8rh51b+gly3BzMwbAxWBXyMWIpqSgnb7K xC9FwBIpze+eDuLLWSqoi8xABulA4L5eWkM+lyZIEP19uRKX/qX+b0esbNtM9TSq+L0i 8v1F+IIevakjCIXE7Npwr4MZyKVP0F36buxLffR1mkNnx5niUQpI3d7YOqUuLNkkLXNJ nV3djME6A3qaP2kkPaSOqayW+RNwlCsBUwjWXkvEJV5eD5HFCpuAh61hAuAiIDLs1tkz aoUJun5EvtpYAWnxExDIDTjeYoZ8nzX2K+hFkrrYyCArgqGGuFIjnUL1CKW4p3bWWU0z H5xg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; t=1680224495; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=FwjqfUNaxStAr97zk6xPM5MCnKIclcyU4ODUMMQVp9o=; b=c3kkaGoGV4D7U2e0K7oBLVojUHzILH7ACuCnGzGfNPH9RdeMmDJW5SSOUbQJkCkV4w fK7uQIMyj2CAnZBmC4oAXhB6cNl6VAiR8vg8WXLo5e6iSLUKqowCskZt/hZ7UZfsP2Qf 55VBTLKxzIjGALhpG8PCG/F5pHaDYzRRWE5l+sSoO0xYHQ3rvN1wdwzah4Xu2v7NI/iB 9C6M3qY8KXIbxzlqZIxB2iXn1fQ8/2mCR/2UroE8cgWzinKHkSCOkiQcU3ucegFeRICZ HpNKeAOxTTfB0nmImggMep7BInR7x7AUX0iGnhdVxn/nW1HQP5tCtLVgPGEgLrTGNjd+ j2iQ== X-Gm-Message-State: AAQBX9eYdtcxE2aNzrhovOmjlwfti9wGx0j0Y5/bBFG5STZJWXPHwaMI LCYrH6AZxOFsc4/9QkGtcNLA8pkJ+Q3r4wqaFfh2vDDoqG1/ktuXzyCuxY4N0VwHyiukoCY8ATn PATdgGq/Jn/iSrbLoZ4A6LLub7OG74F7Ji+6FLUxa4sc1FtSuQ1kKgdVRcVSozdQ= X-Google-Smtp-Source: AKy350ZM9kPx7dtglnLgHuN0DnqorK5wNQoVguGoyXo3G/7YsuuAx3u20BlsLFw7QByydcytejDAgwtrzsHk X-Received: from komlodi.c.googlers.com ([fda3:e722:ac3:cc00:7f:e700:c0a8:35ee]) (user=komlodi job=sendgmr) by 2002:a05:6902:a8e:b0:b7c:1144:a729 with SMTP id cd14-20020a0569020a8e00b00b7c1144a729mr8255690ybb.9.1680224495151; Thu, 30 Mar 2023 18:01:35 -0700 (PDT) Date: Fri, 31 Mar 2023 01:01:16 +0000 In-Reply-To: <20230331010131.1412571-1-komlodi@google.com> Mime-Version: 1.0 References: <20230331010131.1412571-1-komlodi@google.com> X-Mailer: git-send-email 2.40.0.348.gf938b09366-goog Message-ID: <20230331010131.1412571-2-komlodi@google.com> Subject: [PATCH 01/16] hw/misc/aspeed_i3c: Move to i3c directory From: Joe Komlodi To: qemu-devel@nongnu.org Cc: venture@google.com, komlodi@google.com, peter.maydell@linaro.org Received-SPF: pass client-ip=2607:f8b0:4864:20::b4a; envelope-from=37zAmZAcKChI26436v0y66y3w.u648w4C-vwDw3565y5C.69y@flex--komlodi.bounces.google.com; helo=mail-yb1-xb4a.google.com X-Spam_score_int: -95 X-Spam_score: -9.6 X-Spam_bar: --------- X-Spam_report: (-9.6 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_MED=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, USER_IN_DEF_DKIM_WL=-7.5 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+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Moves the Aspeed I3C model and traces into hw/i3c and create I3C build files. Signed-off-by: Joe Komlodi Reviewed-by: Patrick Venture Reviewed-by: Titus Rwantare --- hw/Kconfig | 1 + hw/arm/Kconfig | 1 + hw/i3c/Kconfig | 2 ++ hw/{misc => i3c}/aspeed_i3c.c | 2 +- hw/i3c/meson.build | 3 +++ hw/i3c/trace-events | 7 +++++++ hw/i3c/trace.h | 1 + hw/meson.build | 1 + hw/misc/meson.build | 1 - hw/misc/trace-events | 6 ------ include/hw/arm/aspeed_soc.h | 2 +- include/hw/{misc => i3c}/aspeed_i3c.h | 0 meson.build | 1 + 13 files changed, 19 insertions(+), 9 deletions(-) create mode 100644 hw/i3c/Kconfig rename hw/{misc => i3c}/aspeed_i3c.c (99%) create mode 100644 hw/i3c/meson.build create mode 100644 hw/i3c/trace-events create mode 100644 hw/i3c/trace.h rename include/hw/{misc => i3c}/aspeed_i3c.h (100%) diff --git a/hw/Kconfig b/hw/Kconfig index ba62ff6417..b7c8c24bfd 100644 --- a/hw/Kconfig +++ b/hw/Kconfig @@ -12,6 +12,7 @@ source dma/Kconfig source gpio/Kconfig source hyperv/Kconfig source i2c/Kconfig +source i3c/Kconfig source ide/Kconfig source input/Kconfig source intc/Kconfig diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index b5aed4aff5..a6ca5a9c55 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -454,6 +454,7 @@ config ASPEED_SOC select DS1338 select FTGMAC100 select I2C + select I3C select DPS310 select PCA9552 select SERIAL diff --git a/hw/i3c/Kconfig b/hw/i3c/Kconfig new file mode 100644 index 0000000000..e07fe445c6 --- /dev/null +++ b/hw/i3c/Kconfig @@ -0,0 +1,2 @@ +config I3C + bool diff --git a/hw/misc/aspeed_i3c.c b/hw/i3c/aspeed_i3c.c similarity index 99% rename from hw/misc/aspeed_i3c.c rename to hw/i3c/aspeed_i3c.c index f54f5da522..999978fb7d 100644 --- a/hw/misc/aspeed_i3c.c +++ b/hw/i3c/aspeed_i3c.c @@ -10,7 +10,7 @@ #include "qemu/osdep.h" #include "qemu/log.h" #include "qemu/error-report.h" -#include "hw/misc/aspeed_i3c.h" +#include "hw/i3c/aspeed_i3c.h" #include "hw/registerfields.h" #include "hw/qdev-properties.h" #include "qapi/error.h" diff --git a/hw/i3c/meson.build b/hw/i3c/meson.build new file mode 100644 index 0000000000..c0c38b4c12 --- /dev/null +++ b/hw/i3c/meson.build @@ -0,0 +1,3 @@ +i3c_ss = ss.source_set() +i3c_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('aspeed_i3c.c')) +softmmu_ss.add_all(when: 'CONFIG_I3C', if_true: i3c_ss) diff --git a/hw/i3c/trace-events b/hw/i3c/trace-events new file mode 100644 index 0000000000..3ead84eb45 --- /dev/null +++ b/hw/i3c/trace-events @@ -0,0 +1,7 @@ +# See docs/devel/tracing.rst for syntax documentation. + +# aspeed_i3c.c +aspeed_i3c_read(uint64_t offset, uint64_t data) "I3C read: offset 0x%" PRIx64 " data 0x%" PRIx64 +aspeed_i3c_write(uint64_t offset, uint64_t data) "I3C write: offset 0x%" PRIx64 " data 0x%" PRIx64 +aspeed_i3c_device_read(uint32_t deviceid, uint64_t offset, uint64_t data) "I3C Dev[%u] read: offset 0x%" PRIx64 " data 0x%" PRIx64 +aspeed_i3c_device_write(uint32_t deviceid, uint64_t offset, uint64_t data) "I3C Dev[%u] write: offset 0x%" PRIx64 " data 0x%" PRIx64 diff --git a/hw/i3c/trace.h b/hw/i3c/trace.h new file mode 100644 index 0000000000..9d4a082e19 --- /dev/null +++ b/hw/i3c/trace.h @@ -0,0 +1 @@ +#include "trace/trace-hw_i3c.h" diff --git a/hw/meson.build b/hw/meson.build index c7ac7d3d75..b3225de5f9 100644 --- a/hw/meson.build +++ b/hw/meson.build @@ -12,6 +12,7 @@ subdir('dma') subdir('gpio') subdir('hyperv') subdir('i2c') +subdir('i3c') subdir('ide') subdir('input') subdir('intc') diff --git a/hw/misc/meson.build b/hw/misc/meson.build index a40245ad44..f87c56fb87 100644 --- a/hw/misc/meson.build +++ b/hw/misc/meson.build @@ -115,7 +115,6 @@ softmmu_ss.add(when: 'CONFIG_PVPANIC_PCI', if_true: files('pvpanic-pci.c')) softmmu_ss.add(when: 'CONFIG_AUX', if_true: files('auxbus.c')) softmmu_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files( 'aspeed_hace.c', - 'aspeed_i3c.c', 'aspeed_lpc.c', 'aspeed_scu.c', 'aspeed_sbc.c', diff --git a/hw/misc/trace-events b/hw/misc/trace-events index c47876a902..cc335fc89d 100644 --- a/hw/misc/trace-events +++ b/hw/misc/trace-events @@ -205,12 +205,6 @@ armsse_mhu_write(uint64_t offset, uint64_t data, unsigned size) "SSE-200 MHU wri # aspeed_xdma.c aspeed_xdma_write(uint64_t offset, uint64_t data) "XDMA write: offset 0x%" PRIx64 " data 0x%" PRIx64 -# aspeed_i3c.c -aspeed_i3c_read(uint64_t offset, uint64_t data) "I3C read: offset 0x%" PRIx64 " data 0x%" PRIx64 -aspeed_i3c_write(uint64_t offset, uint64_t data) "I3C write: offset 0x%" PRIx64 " data 0x%" PRIx64 -aspeed_i3c_device_read(uint32_t deviceid, uint64_t offset, uint64_t data) "I3C Dev[%u] read: offset 0x%" PRIx64 " data 0x%" PRIx64 -aspeed_i3c_device_write(uint32_t deviceid, uint64_t offset, uint64_t data) "I3C Dev[%u] write: offset 0x%" PRIx64 " data 0x%" PRIx64 - # aspeed_sdmc.c aspeed_sdmc_write(uint64_t reg, uint64_t data) "reg @0x%" PRIx64 " data: 0x%" PRIx64 aspeed_sdmc_read(uint64_t reg, uint64_t data) "reg @0x%" PRIx64 " data: 0x%" PRIx64 diff --git a/include/hw/arm/aspeed_soc.h b/include/hw/arm/aspeed_soc.h index 8adff70072..603cebe5b3 100644 --- a/include/hw/arm/aspeed_soc.h +++ b/include/hw/arm/aspeed_soc.h @@ -22,7 +22,7 @@ #include "hw/timer/aspeed_timer.h" #include "hw/rtc/aspeed_rtc.h" #include "hw/i2c/aspeed_i2c.h" -#include "hw/misc/aspeed_i3c.h" +#include "hw/i3c/aspeed_i3c.h" #include "hw/ssi/aspeed_smc.h" #include "hw/misc/aspeed_hace.h" #include "hw/misc/aspeed_sbc.h" diff --git a/include/hw/misc/aspeed_i3c.h b/include/hw/i3c/aspeed_i3c.h similarity index 100% rename from include/hw/misc/aspeed_i3c.h rename to include/hw/i3c/aspeed_i3c.h diff --git a/meson.build b/meson.build index 29f8644d6d..91f9b4bbe7 100644 --- a/meson.build +++ b/meson.build @@ -2979,6 +2979,7 @@ if have_system 'hw/dma', 'hw/hyperv', 'hw/i2c', + 'hw/i3c', 'hw/i386', 'hw/i386/xen', 'hw/i386/kvm', From patchwork Fri Mar 31 01:01:17 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Joe Komlodi X-Patchwork-Id: 1763554 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=nongnu.org (client-ip=209.51.188.17; helo=lists.gnu.org; envelope-from=qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org; receiver=) Authentication-Results: legolas.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=google.com header.i=@google.com header.a=rsa-sha256 header.s=20210112 header.b=rRJvJWsA; dkim-atps=neutral Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-ECDSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4Pnhqj2jlPz1yYr for ; Fri, 31 Mar 2023 12:03:29 +1100 (AEDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1pi39H-0003Cm-7L; Thu, 30 Mar 2023 21:01:47 -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 <38DAmZAcKChM37547w1z77z4x.v759x5D-wxEx4676z6D.7Az@flex--komlodi.bounces.google.com>) id 1pi39E-0003BT-3l for qemu-devel@nongnu.org; Thu, 30 Mar 2023 21:01:44 -0400 Received: from mail-pf1-x449.google.com ([2607:f8b0:4864:20::449]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from <38DAmZAcKChM37547w1z77z4x.v759x5D-wxEx4676z6D.7Az@flex--komlodi.bounces.google.com>) id 1pi39A-0006a0-I3 for qemu-devel@nongnu.org; Thu, 30 Mar 2023 21:01:43 -0400 Received: by mail-pf1-x449.google.com with SMTP id b8-20020aa78708000000b005eaa50faa35so9796300pfo.20 for ; Thu, 30 Mar 2023 18:01:38 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20210112; t=1680224496; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=FNR2btRSIHAM/N5/5N+KWrRsj2p1b78b3jBEVu2PZkE=; b=rRJvJWsA7IpV0J2JTMOgpanZ5Cnowjtu2dxn/zXBYSRWrktM0N4pTCzwN6rLeawlEZ 9OF7/0LzSy1qoTlZknwZP1/scLTIZkhvYgVUDja92bG0Xlv3omTi0Pk7nNH9xoKzvSEa Ye7tjZd9RBOl7FkuQ6yokoQA02BwLHwGPlE0cuoOFqLfF2dRo7mAGNLK1GULOFaaQZiv xA0gWmYhURI3Ltht0V+DYjVG3gd5bDrbXQ9L7BicLDRGvCCUsDRoHaDgKt5AEl5eTnxi R+ikwmXBsdN67XMpQ8LDHrP9JI8+TYt+V1slvEznySL35mEHK/F64311c+ObiYpoO+2v UFug== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; t=1680224496; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=FNR2btRSIHAM/N5/5N+KWrRsj2p1b78b3jBEVu2PZkE=; b=uuqWPDrXC9yTT4QH8WLV1TrKFkUu4g0S1EdZ2eSXT6sW7uJ21BKriLv96pGsfV8aYg /kVMJVH5z41gRVp8sKoXPX75d4uvLw/IMyLoetRxcN0kV+gKiRvsAaEo3uqsM5r7sSQJ DbLDgIOyRZZwud/66tRZ/gZ5EHe8q1/d05KmY4ri+oqpgw+wwPF+KFMlmUhnOYGOB3pB eSBMqmX3kR47RjEJzvbI6wAjQfhw2cIsURxICy+q/RlRKxJtaN24VtQjcwP/UnGJTjRg gNziAUpTmKu9I+YmjFZey2wP+nN0nWIpuwt0xLdG87r4pgrhdNtwEElF3+fqkm3ywE2y yhLg== X-Gm-Message-State: AAQBX9f2F0x4xH02JlHjE6Ajgbk9MQDhqk1r56p6EHSingAdOBk2Hs10 56sW4nU9iWyw4yUnHPrIvBJLqorMrOtjrf1hevm7WrVYF3GvLxKaK10wdSUkypj2p3bPNqFTp8D PD1lPuDk4sHQjXzpGdFzjDWlN0vYojy1eUgj8EpLH63WCRakPYClmmRf69z1Fn2Y= X-Google-Smtp-Source: AKy350ZHuxry7XcXr9tMRg9AAzkgGFVCoPZbpzvdTgPOcPt+XhhDW5Ijij3yDc5jpfkNR33SvyVqfNdHRobe X-Received: from komlodi.c.googlers.com ([fda3:e722:ac3:cc00:7f:e700:c0a8:35ee]) (user=komlodi job=sendgmr) by 2002:a17:902:dac9:b0:1a2:1c7:1c1f with SMTP id q9-20020a170902dac900b001a201c71c1fmr9786511plx.7.1680224496709; Thu, 30 Mar 2023 18:01:36 -0700 (PDT) Date: Fri, 31 Mar 2023 01:01:17 +0000 In-Reply-To: <20230331010131.1412571-1-komlodi@google.com> Mime-Version: 1.0 References: <20230331010131.1412571-1-komlodi@google.com> X-Mailer: git-send-email 2.40.0.348.gf938b09366-goog Message-ID: <20230331010131.1412571-3-komlodi@google.com> Subject: [PATCH 02/16] hw/i3c: Add bus support From: Joe Komlodi To: qemu-devel@nongnu.org Cc: venture@google.com, komlodi@google.com, peter.maydell@linaro.org Received-SPF: pass client-ip=2607:f8b0:4864:20::449; envelope-from=38DAmZAcKChM37547w1z77z4x.v759x5D-wxEx4676z6D.7Az@flex--komlodi.bounces.google.com; helo=mail-pf1-x449.google.com X-Spam_score_int: -95 X-Spam_score: -9.6 X-Spam_bar: --------- X-Spam_report: (-9.6 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_MED=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, USER_IN_DEF_DKIM_WL=-7.5 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+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Adds an I3C bus and a target class. The bus supports: - I3C data transmission and reception - CCCs (including ENTDAA) - IBIs - legacy I2C transactions General usage of the bus is similar to I2C. Users are expected to initialize a bus via i3c_init_bus, and use the bus returned from the init function to do transactions on the bus. In order to handle IBIs, the controller provides callbacks to handle receiving an IBI from a target, receiving (optional) additional IBI bytes from a target, and handling when a target is done with its IBI. Similarly, target creation is done via i3c_target_create_simple and users use the provided I3CTarget to handle transactions. The target has functions provided that it can use to invoke an IBI and send additional bytes. Along with the expected, send, recv, and event callbacks that is expected of an I3C target, which are similar to I2C, there is a separate callback for CCC handling. This is to help encapsulate CCC handling and keep it separate from target-specific read/write functionality. To avoid repition for required CCCs among I3C targets, there is some class-level CCC handling added. The CCC is then passed to the target in case it needs to handle it in some way. Signed-off-by: Joe Komlodi Reviewed-by: Patrick Venture Reviewed-by: Titus Rwantare --- hw/i3c/core.c | 629 +++++++++++++++++++++++++++++++++++++++++++ hw/i3c/meson.build | 1 + hw/i3c/trace-events | 16 ++ include/hw/i3c/i3c.h | 275 +++++++++++++++++++ 4 files changed, 921 insertions(+) create mode 100644 hw/i3c/core.c create mode 100644 include/hw/i3c/i3c.h diff --git a/hw/i3c/core.c b/hw/i3c/core.c new file mode 100644 index 0000000000..758dd7141f --- /dev/null +++ b/hw/i3c/core.c @@ -0,0 +1,629 @@ +/* + * QEMU I3C bus interface. + * + * Copyright 2023 Google LLC + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 General Public License + * for more details. + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qapi/error.h" +#include "trace.h" +#include "hw/i3c/i3c.h" +#include "hw/qdev-properties.h" + +static Property i3c_props[] = { + DEFINE_PROP_UINT8("static-address", struct I3CTarget, static_address, 0), + DEFINE_PROP_UINT8("dcr", struct I3CTarget, dcr, 0), + DEFINE_PROP_UINT8("bcr", struct I3CTarget, bcr, 0), + DEFINE_PROP_UINT64("pid", struct I3CTarget, pid, 0), + DEFINE_PROP_END_OF_LIST(), +}; + +static const TypeInfo i3c_bus_info = { + .name = TYPE_I3C_BUS, + .parent = TYPE_BUS, + .instance_size = sizeof(I3CBus), + .class_size = sizeof(I3CBusClass), +}; + +I3CBus *i3c_init_bus(DeviceState *parent, const char *name) +{ + return i3c_init_bus_type(TYPE_I3C_BUS, parent, name); +} + +I3CBus *i3c_init_bus_type(const char *type, DeviceState *parent, + const char *name) +{ + I3CBus *bus; + + bus = I3C_BUS(qbus_new(type, parent, name)); + QLIST_INIT(&bus->current_devs); + bus->broadcast = false; + bus->in_entdaa = false; + bus->in_ccc = false; + + /* I2C init. */ + g_autofree gchar *i2c_bus_name = g_strdup_printf("%s-legacy-i2c", name); + bus->i2c_bus = i2c_init_bus(parent, i2c_bus_name); + + return bus; +} + +bool i3c_bus_busy(I3CBus *bus) +{ + return !QLIST_EMPTY(&bus->current_devs); +} + +bool i3c_target_match(I3CBus *bus, I3CTarget *target, uint8_t address) +{ + /* Once a target has a dynamic address, it only responds to that. */ + uint8_t targ_addr = target->address ? target->address : + target->static_address; + + if (bus->in_entdaa) { + if (address != I3C_BROADCAST) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: I3C Address 0x%.2x sent during " + "ENTDAA instead of a broadcast address\n", + object_get_canonical_path(OBJECT(bus)), address); + return false; + } + + /* + * Targets should only ACK ENTDAA broadcasts if they have no dynamic + * address. + */ + if (target->address == 0) { + I3CNode *node = g_new(struct I3CNode, 1); + node->target = target; + QLIST_INSERT_HEAD(&bus->current_devs, node, next); + } + return target->address == 0; + } + + if ((targ_addr == address) || bus->broadcast) { + I3CNode *node = g_new(struct I3CNode, 1); + node->target = target; + QLIST_INSERT_HEAD(&bus->current_devs, node, next); + return true; + } + + return false; +} + +bool i3c_scan_bus(I3CBus *bus, uint8_t address) +{ + BusChild *child; + I3CNode *node, *next; + + /* Clear out any devices from a previous (re-)START. */ + QLIST_FOREACH_SAFE(node, &bus->current_devs, next, next) { + QLIST_REMOVE(node, next); + g_free(node); + } + + QTAILQ_FOREACH(child, &bus->qbus.children, sibling) { + DeviceState *qdev = child->child; + I3CTarget *target = I3C_TARGET(qdev); + + if (i3c_target_match(bus, target, address)) { + return true; + } + } + + /* No one on the bus could respond. */ + return false; +} + +/* Class-level event handling, since we do some CCCs at the class level. */ +static int i3c_target_event(I3CTarget *t, enum I3CEvent event) +{ + I3CTargetClass *tc = I3C_TARGET_GET_CLASS(t); + trace_i3c_target_event(t->address, event); + + if (event == I3C_STOP) { + t->curr_ccc = 0; + t->ccc_byte_offset = 0; + t->in_ccc = false; + } + return tc->event(t, event); +} + +/* + * Sends a START or repeated START and the address for an I3C transaction. + * + * This function returns 0 if a device on the bus was able to respond to the + * address, and non-zero otherwise. + * A non-zero return represents a NACK. + */ +static int i3c_do_start_transfer(I3CBus *bus, uint8_t address, + enum I3CEvent event) +{ + I3CTargetClass *tc; + I3CNode *node; + + if (address == I3C_BROADCAST) { + bus->broadcast = true; + /* If we're not in ENTDAA, a broadcast is the start of a new CCC. */ + if (!bus->in_entdaa) { + bus->in_ccc = false; + } + } else { + bus->broadcast = false; + } + + /* No one responded to the address, NACK it. */ + if (!i3c_scan_bus(bus, address)) { + return -1; + } + + QLIST_FOREACH(node, &bus->current_devs, next) { + I3CTarget *t = node->target; + + tc = I3C_TARGET_GET_CLASS(t); + if (tc->event) { + int rv = i3c_target_event(t, event); + if (rv && !bus->broadcast) { + return rv; + } + } + } + + return 0; +} + +int i3c_start_transfer(I3CBus *bus, uint8_t address, bool is_recv) +{ + trace_i3c_start_transfer(address, is_recv); + return i3c_do_start_transfer(bus, address, is_recv + ? I3C_START_RECV + : I3C_START_SEND); +} + +int i3c_start_recv(I3CBus *bus, uint8_t address) +{ + trace_i3c_start_transfer(address, true); + return i3c_do_start_transfer(bus, address, I3C_START_RECV); +} + +int i3c_start_send(I3CBus *bus, uint8_t address) +{ + trace_i3c_start_transfer(address, false); + return i3c_do_start_transfer(bus, address, I3C_START_SEND); +} + +void i3c_end_transfer(I3CBus *bus) +{ + I3CTargetClass *tc; + I3CNode *node, *next; + + trace_i3c_end_transfer(); + + /* + * If we're in ENTDAA, we need to notify all devices when ENTDAA is done. + * This is because everyone initially participates due to the broadcast, + * but gradually drops out as they get assigned addresses. + * Since the current_devs list only stores who's currently participating, + * and not everyone who previously participated, we send the STOP to all + * children. + */ + if (bus->in_entdaa) { + BusChild *child; + + QTAILQ_FOREACH(child, &bus->qbus.children, sibling) { + DeviceState *qdev = child->child; + I3CTarget *t = I3C_TARGET(qdev); + tc = I3C_TARGET_GET_CLASS(t); + if (tc->event) { + i3c_target_event(t, I3C_STOP); + } + } + } else { + QLIST_FOREACH_SAFE(node, &bus->current_devs, next, next) { + I3CTarget *t = node->target; + tc = I3C_TARGET_GET_CLASS(t); + if (tc->event) { + i3c_target_event(t, I3C_STOP); + } + QLIST_REMOVE(node, next); + g_free(node); + } + } + bus->broadcast = false; + bus->in_entdaa = false; + bus->in_ccc = false; +} + +/* + * Any CCCs that are universal across all I3C devices should be handled here. + * Once they're handled, we pass the CCC up to the I3C target to do anything + * else it may want with the bytes. + */ +static int i3c_target_handle_ccc_write(I3CTarget *t, const uint8_t *data, + uint32_t num_to_send, uint32_t *num_sent) +{ + I3CTargetClass *tc = I3C_TARGET_GET_CLASS(t); + *num_sent = 0; + + /* Is this the start of a new CCC? */ + if (!t->in_ccc) { + t->curr_ccc = *data; + t->in_ccc = true; + *num_sent = 1; + trace_i3c_target_handle_ccc(t->address, t->curr_ccc); + } + + switch (t->curr_ccc) { + case I3C_CCC_ENTDAA: + /* + * This is the last byte of ENTDAA, the controller is assigning us an + * address. + */ + if (t->ccc_byte_offset == 8) { + t->address = *data; + t->in_ccc = false; + t->curr_ccc = 0; + t->ccc_byte_offset = 0; + *num_sent = 1; + } + break; + case I3C_CCCD_SETDASA: + t->address = t->static_address; + break; + case I3C_CCC_SETAASA: + t->address = t->static_address; + break; + case I3C_CCC_RSTDAA: + t->address = 0; + break; + case I3C_CCCD_SETNEWDA: + /* If this isn't the CCC byte, it's our new address. */ + if (*num_sent == 0) { + t->address = *data; + *num_sent = 1; + } + break; + /* Ignore other CCCs it's better to handle on a device-by-device basis. */ + default: + break; + } + return tc->handle_ccc_write(t, data, num_to_send, num_sent); +} + +int i3c_send_byte(I3CBus *bus, uint8_t data) +{ + /* + * Ignored, the caller can determine how many were sent based on if this was + * ACKed/NACKed. + */ + uint32_t num_sent; + return i3c_send(bus, &data, 1, &num_sent); +} + +int i3c_send(I3CBus *bus, const uint8_t *data, uint32_t num_to_send, + uint32_t *num_sent) +{ + I3CTargetClass *tc; + I3CTarget *t; + I3CNode *node; + int ret = 0; + + /* If this message is a broadcast and no CCC has been found, grab it. */ + if (bus->broadcast && !bus->in_ccc) { + bus->ccc = *data; + bus->in_ccc = true; + /* + * We need to keep track if we're currently in ENTDAA. + * On any other CCC, the CCC is over on a RESTART or STOP, but ENTDAA + * is only over on a STOP. + */ + if (bus->ccc == I3C_CCC_ENTDAA) { + bus->in_entdaa = true; + } + } + + QLIST_FOREACH(node, &bus->current_devs, next) { + t = node->target; + tc = I3C_TARGET_GET_CLASS(t); + if (bus->in_ccc) { + if (!tc->handle_ccc_write) { + ret = -1; + continue; + } + ret = i3c_target_handle_ccc_write(t, data, num_to_send, num_sent); + /* Targets should only NACK on a direct CCC. */ + if (ret && !CCC_IS_DIRECT(bus->ccc)) { + ret = 0; + } + } else { + if (tc->send) { + ret = ret || tc->send(t, data, num_to_send, num_sent); + } else { + ret = -1; + } + } + } + + trace_i3c_send(*num_sent, num_to_send, ret == 0); + + return ret ? -1 : 0; +} + +static int i3c_target_handle_ccc_read(I3CTarget *t, uint8_t *data, + uint32_t num_to_read, uint32_t *num_read) +{ + I3CTargetClass *tc = I3C_TARGET_GET_CLASS(t); + uint8_t read_count = 0; + + switch (t->curr_ccc) { + case I3C_CCC_ENTDAA: + /* Return the 6-byte PID, followed by BCR then DCR. */ + while (t->ccc_byte_offset < 6) { + if (read_count >= num_to_read) { + break; + } + data[read_count] = (t->pid >> (t->ccc_byte_offset * 8)) & 0xff; + t->ccc_byte_offset++; + read_count++; + } + if (read_count < num_to_read) { + data[read_count] = t->bcr; + t->ccc_byte_offset++; + read_count++; + } + if (read_count < num_to_read) { + data[read_count] = t->dcr; + t->ccc_byte_offset++; + read_count++; + } + *num_read = read_count; + break; + case I3C_CCCD_GETPID: + while (t->ccc_byte_offset < 6) { + if (read_count >= num_to_read) { + break; + } + data[read_count] = (t->pid >> (t->ccc_byte_offset * 8)) & 0xff; + t->ccc_byte_offset++; + read_count++; + } + *num_read = read_count; + break; + case I3C_CCCD_GETBCR: + *data = t->bcr; + *num_read = 1; + break; + case I3C_CCCD_GETDCR: + *data = t->dcr; + *num_read = 1; + break; + default: + /* Unhandled on the I3CTarget class level. */ + break; + } + + return tc->handle_ccc_read(t, data, num_to_read, num_read); +} + +int i3c_recv_byte(I3CBus *bus, uint8_t *data) +{ + /* + * Ignored, the caller can determine how many bytes were read based on if + * this is ACKed/NACKed. + */ + uint32_t num_read; + return i3c_recv(bus, data, 1, &num_read); +} + +int i3c_recv(I3CBus *bus, uint8_t *data, uint32_t num_to_read, + uint32_t *num_read) +{ + int ret = 0; + I3CTargetClass *tc; + I3CTarget *t; + + *data = 0xff; + if (!QLIST_EMPTY(&bus->current_devs)) { + tc = I3C_TARGET_GET_CLASS(QLIST_FIRST(&bus->current_devs)->target); + t = QLIST_FIRST(&bus->current_devs)->target; + if (bus->in_ccc) { + if (!tc->handle_ccc_read) { + return -1; + } + ret = i3c_target_handle_ccc_read(t, data, num_to_read, num_read); + } else { + if (tc->recv) { + /* + * Targets cannot NACK on a direct transfer, so the data + * is returned directly. + */ + *num_read = tc->recv(t, data, num_to_read); + } + } + } + + trace_i3c_recv(*num_read, num_to_read, ret == 0); + + return ret; +} + +void i3c_nack(I3CBus *bus) +{ + I3CTargetClass *tc; + I3CNode *node; + + if (QLIST_EMPTY(&bus->current_devs)) { + return; + } + + QLIST_FOREACH(node, &bus->current_devs, next) { + tc = I3C_TARGET_GET_CLASS(node->target); + if (tc->event) { + i3c_target_event(node->target, I3C_NACK); + } + } +} + +int i3c_target_send_ibi(I3CTarget *t, uint8_t addr, bool is_recv) +{ + I3CBus *bus = I3C_BUS(t->qdev.parent_bus); + I3CBusClass *bc = I3C_BUS_GET_CLASS(bus); + trace_i3c_target_send_ibi(addr, is_recv); + return bc->ibi_handle(bus, t, addr, is_recv); +} + +int i3c_target_send_ibi_bytes(I3CTarget *t, uint8_t data) +{ + I3CBus *bus = I3C_BUS(t->qdev.parent_bus); + I3CBusClass *bc = I3C_BUS_GET_CLASS(bus); + trace_i3c_target_send_ibi_bytes(data); + return bc->ibi_recv(bus, data); +} + +int i3c_target_ibi_finish(I3CTarget *t, uint8_t data) +{ + I3CBus *bus = I3C_BUS(t->qdev.parent_bus); + I3CBusClass *bc = I3C_BUS_GET_CLASS(bus); + trace_i3c_target_ibi_finish(); + return bc->ibi_finish(bus); +} + +static bool i3c_addr_is_rsvd(uint8_t addr) +{ + const bool is_rsvd[255] = { + [0x00] = true, + [0x01] = true, + [0x02] = true, + [0x3e] = true, + [0x5e] = true, + [0x6e] = true, + [0x76] = true, + [0x7a] = true, + [0x7c] = true, + [0x7e] = true, + [0x7f] = true, + }; + + return is_rsvd[addr]; +} + +I3CTarget *i3c_target_new(const char *name, uint8_t addr, uint8_t dcr, + uint8_t bcr, uint64_t pid) +{ + DeviceState *dev; + + dev = qdev_new(name); + qdev_prop_set_uint8(dev, "static-address", addr); + qdev_prop_set_uint8(dev, "dcr", dcr); + qdev_prop_set_uint8(dev, "bcr", bcr); + qdev_prop_set_uint64(dev, "pid", pid); + + if (i3c_addr_is_rsvd(addr)) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: I3C target created with reserved " + "address 0x%.2x\n", + object_get_canonical_path(OBJECT(dev)), addr); + } + return I3C_TARGET(dev); +} + +bool i3c_target_realize_and_unref(I3CTarget *dev, I3CBus *bus, Error **errp) +{ + return qdev_realize_and_unref(&dev->qdev, &bus->qbus, errp); +} + +I3CTarget *i3c_target_create_simple(I3CBus *bus, const char *name, uint8_t addr, + uint8_t dcr, uint8_t bcr, uint64_t pid) +{ + I3CTarget *dev = i3c_target_new(name, addr, dcr, bcr, pid); + dev->address = 0; + i3c_target_realize_and_unref(dev, bus, &error_abort); + + return dev; +} + +/* Legacy I2C functions. */ +void legacy_i2c_nack(I3CBus *bus) +{ + trace_legacy_i2c_nack(); + i2c_nack(bus->i2c_bus); +} + +uint8_t legacy_i2c_recv(I3CBus *bus) +{ + uint8_t byte = i2c_recv(bus->i2c_bus); + trace_legacy_i2c_recv(byte); + return byte; +} + +int legacy_i2c_send(I3CBus *bus, uint8_t data) +{ + trace_legacy_i2c_send(data); + return i2c_send(bus->i2c_bus, data); +} + +int legacy_i2c_start_transfer(I3CBus *bus, uint8_t address, bool is_recv) +{ + trace_legacy_i2c_start_transfer(address, is_recv); + return i2c_start_transfer(bus->i2c_bus, address, is_recv); +} + +int legacy_i2c_start_recv(I3CBus *bus, uint8_t address) +{ + trace_legacy_i2c_start_transfer(address, true); + return i2c_start_transfer(bus->i2c_bus, address, /*is_recv=*/true); +} + +int legacy_i2c_start_send(I3CBus *bus, uint8_t address) +{ + trace_legacy_i2c_start_transfer(address, false); + return i2c_start_transfer(bus->i2c_bus, address, /*is_recv=*/false); +} + +void legacy_i2c_end_transfer(I3CBus *bus) +{ + trace_legacy_i2c_end_transfer(); + i2c_end_transfer(bus->i2c_bus); +} + +I2CSlave *legacy_i2c_device_create_simple(I3CBus *bus, const char *name, + uint8_t addr) +{ + I2CSlave *dev = i2c_slave_new(name, addr); + + i2c_slave_realize_and_unref(dev, bus->i2c_bus, &error_abort); + return dev; +} + +static void i3c_target_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *k = DEVICE_CLASS(klass); + set_bit(DEVICE_CATEGORY_MISC, k->categories); + k->bus_type = TYPE_I3C_BUS; + device_class_set_props(k, i3c_props); +} + +static const TypeInfo i3c_target_type_info = { + .name = TYPE_I3C_TARGET, + .parent = TYPE_DEVICE, + .instance_size = sizeof(I3CTarget), + .abstract = true, + .class_size = sizeof(I3CTargetClass), + .class_init = i3c_target_class_init, +}; + +static void i3c_register_types(void) +{ + type_register_static(&i3c_bus_info); + type_register_static(&i3c_target_type_info); +} + +type_init(i3c_register_types) diff --git a/hw/i3c/meson.build b/hw/i3c/meson.build index c0c38b4c12..3599f1a396 100644 --- a/hw/i3c/meson.build +++ b/hw/i3c/meson.build @@ -1,3 +1,4 @@ i3c_ss = ss.source_set() +i3c_ss.add(when: 'CONFIG_I3C', if_true: files('core.c')) i3c_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('aspeed_i3c.c')) softmmu_ss.add_all(when: 'CONFIG_I3C', if_true: i3c_ss) diff --git a/hw/i3c/trace-events b/hw/i3c/trace-events index 3ead84eb45..cdf7cb07f6 100644 --- a/hw/i3c/trace-events +++ b/hw/i3c/trace-events @@ -5,3 +5,19 @@ aspeed_i3c_read(uint64_t offset, uint64_t data) "I3C read: offset 0x%" PRIx64 " aspeed_i3c_write(uint64_t offset, uint64_t data) "I3C write: offset 0x%" PRIx64 " data 0x%" PRIx64 aspeed_i3c_device_read(uint32_t deviceid, uint64_t offset, uint64_t data) "I3C Dev[%u] read: offset 0x%" PRIx64 " data 0x%" PRIx64 aspeed_i3c_device_write(uint32_t deviceid, uint64_t offset, uint64_t data) "I3C Dev[%u] write: offset 0x%" PRIx64 " data 0x%" PRIx64 + +# core.c +i3c_target_event(uint8_t address, uint8_t event) "I3C target 0x%" PRIx8 " event 0x%" PRIx8 +i3c_target_handle_ccc(uint8_t address, uint8_t ccc) "I3C target 0x%" PRIx8 " handling CCC 0x%" PRIx8 +i3c_target_send_ibi(uint8_t address, bool is_recv) "I3C target IBI address 0x%" PRIx8 " RnW=%d" +i3c_target_send_ibi_bytes(uint8_t byte) "I3C target IBI byte 0x%" PRIx8 +i3c_target_ibi_finish(void) "I3C target IBI finish" +i3c_start_transfer(uint8_t address, bool is_recv) "I3C START with address 0x%" PRIx8 " is_recv=%d" +i3c_end_transfer(void) "I3C transfer done" +i3c_send(uint32_t num_sent, uint32_t num_to_send, bool ack) "I3C send %" PRId32 "/%" PRId32 " bytes, ack=%d" +i3c_recv(uint32_t num_read, uint32_t num_to_read, bool ack) "I3C recv %" PRId32 "/%" PRId32 " bytes, ack=%d" +legacy_i2c_nack(void) "Legacy I2C NACK" +legacy_i2c_recv(uint8_t byte) "Legacy I2C recv 0x%" PRIx8 +legacy_i2c_send(uint8_t byte) "Legacy I2C send 0x%" PRIx8 +legacy_i2c_start_transfer(uint8_t address, bool is_recv) "Legacy I2C START with address 0x%" PRIx8 " is_recv=%d" +legacy_i2c_end_transfer(void) "Legacy I2C STOP" diff --git a/include/hw/i3c/i3c.h b/include/hw/i3c/i3c.h new file mode 100644 index 0000000000..dab74a0e93 --- /dev/null +++ b/include/hw/i3c/i3c.h @@ -0,0 +1,275 @@ +/* + * QEMU I3C bus interface. + * + * Copyright 2023 Google LLC + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 General Public License + * for more details. + */ + +#ifndef QEMU_INCLUDE_HW_I3C_I3C_H_ +#define QEMU_INCLUDE_HW_I3C_I3C_H_ + +#include "hw/qdev-core.h" +#include "qom/object.h" +#include "hw/i2c/i2c.h" + +#define TYPE_I3C_TARGET "i3c-target" +OBJECT_DECLARE_TYPE(I3CTarget, I3CTargetClass, I3C_TARGET) + +typedef enum I3CEvent { + I3C_START_RECV, + I3C_START_SEND, + I3C_STOP, + I3C_NACK, +} I3CEvent; + +typedef enum I3CCCC { + /* Broadcast CCCs */ + I3C_CCC_ENEC = 0x00, + I3C_CCC_DISEC = 0x01, + I3C_CCC_ENTAS0 = 0x02, + I3C_CCC_ENTAS1 = 0x03, + I3C_CCC_ENTAS2 = 0x04, + I3C_CCC_ENTAS3 = 0x05, + I3C_CCC_RSTDAA = 0x06, + I3C_CCC_ENTDAA = 0x07, + I3C_CCC_DEFTGTS = 0x08, + I3C_CCC_SETMWL = 0x09, + I3C_CCC_SETMRL = 0x0a, + I3C_CCC_ENTTM = 0x0b, + I3C_CCC_SETBUSCON = 0x0c, + I3C_CCC_ENDXFER = 0x12, + I3C_CCC_ENTHDR0 = 0x20, + I3C_CCC_ENTHDR1 = 0x21, + I3C_CCC_ENTHDR2 = 0x22, + I3C_CCC_ENTHDR3 = 0x23, + I3C_CCC_ENTHDR4 = 0x24, + I3C_CCC_ENTHDR5 = 0x25, + I3C_CCC_ENTHDR6 = 0x26, + I3C_CCC_ENTHDR7 = 0x27, + I3C_CCC_SETXTIME = 0x28, + I3C_CCC_SETAASA = 0x29, + I3C_CCC_RSTACT = 0x2a, + I3C_CCC_DEFGRPA = 0x2b, + I3C_CCC_RSTGRPA = 0x2c, + I3C_CCC_MLANE = 0x2d, + /* Direct CCCs */ + I3C_CCCD_ENEC = 0x80, + I3C_CCCD_DISEC = 0x81, + I3C_CCCD_ENTAS0 = 0x82, + I3C_CCCD_ENTAS1 = 0x83, + I3C_CCCD_ENTAS2 = 0x84, + I3C_CCCD_ENTAS3 = 0x85, + I3C_CCCD_SETDASA = 0x87, + I3C_CCCD_SETNEWDA = 0x88, + I3C_CCCD_SETMWL = 0x89, + I3C_CCCD_SETMRL = 0x8a, + I3C_CCCD_GETMWL = 0x8b, + I3C_CCCD_GETMRL = 0x8c, + I3C_CCCD_GETPID = 0x8d, + I3C_CCCD_GETBCR = 0x8e, + I3C_CCCD_GETDCR = 0x8f, + I3C_CCCD_GETSTATUS = 0x90, + I3C_CCCD_GETACCCR = 0x91, + I3C_CCCD_ENDXFER = 0x92, + I3C_CCCD_SETBRGTGT = 0x93, + I3C_CCCD_GETMXDS = 0x94, + I3C_CCCD_GETCAPS = 0x95, + I3C_CCCD_SETROUTE = 0x96, + I3C_CCCD_SETXTIME = 0x98, + I3C_CCCD_GETXTIME = 0x99, + I3C_CCCD_RSTACT = 0x9a, + I3C_CCCD_SETGRPA = 0x9b, + I3C_CCCD_RSTGRPA = 0x9c, + I3C_CCCD_MLANE = 0x9d, +} I3CCCC; + +#define CCC_IS_DIRECT(_ccc) (_ccc & 0x80) + +#define I3C_BROADCAST 0x7e +#define I3C_HJ_ADDR 0x02 +#define I3C_ENTDAA_SIZE 8 + +struct I3CTargetClass { + DeviceClass parent; + + /* + * Controller to target. Returns 0 for success, non-zero for NAK or other + * error. + */ + int (*send)(I3CTarget *s, const uint8_t *data, uint32_t num_to_send, + uint32_t *num_sent); + /* + * Target to controller. I3C targets are able to terminate reads early, so + * this returns the number of bytes read from the target. + */ + uint32_t (*recv)(I3CTarget *s, uint8_t *data, uint32_t num_to_read); + /* Notify the target of a bus state change. */ + int (*event)(I3CTarget *s, enum I3CEvent event); + /* + * Handle a read CCC transmitted from a controller. + * CCCs are I3C commands that I3C targets support. + * The target can NACK the CCC if it does not support it. + */ + int (*handle_ccc_read)(I3CTarget *s, uint8_t *data, uint32_t num_to_read, + uint32_t *num_read); + /* + * Handle a write CCC transmitted from a controller. + * CCCs are I3C commands that I3C targets support. + * The target can NACK the CCC if it does not support it. + */ + int (*handle_ccc_write)(I3CTarget *s, const uint8_t *data, + uint32_t num_to_send, uint32_t *num_sent); +}; + +struct I3CTarget { + DeviceState qdev; + + uint8_t address; + uint8_t static_address; + uint8_t dcr; + uint8_t bcr; + uint64_t pid; + + /* CCC State tracking. */ + I3CCCC curr_ccc; + uint8_t ccc_byte_offset; + bool in_ccc; +}; + +struct I3CNode { + I3CTarget *target; + QLIST_ENTRY(I3CNode) next; +}; + +typedef struct I3CNode I3CNode; + +typedef QLIST_HEAD(I3CNodeList, I3CNode) I3CNodeList; + +#define TYPE_I3C_BUS "i3c-bus" +OBJECT_DECLARE_TYPE(I3CBus, I3CBusClass, I3C_BUS) + +struct I3CBus { + BusState qbus; + + /* Legacy I2C. */ + I2CBus *i2c_bus; + + I3CNodeList current_devs; + bool broadcast; + uint8_t ccc; + bool in_ccc; + bool in_entdaa; + uint8_t saved_address; +}; + +struct I3CBusClass { + DeviceClass parent; + + /* Handle an incoming IBI request from a target */ + int (*ibi_handle) (I3CBus *bus, I3CTarget *target, uint8_t addr, + bool is_recv); + /* Receive data from an IBI request */ + int (*ibi_recv) (I3CBus *bus, uint8_t data); + /* Do anything that needs to be done, since the IBI is finished. */ + int (*ibi_finish) (I3CBus *bus); +}; + +I3CBus *i3c_init_bus(DeviceState *parent, const char *name); +I3CBus *i3c_init_bus_type(const char *type, DeviceState *parent, + const char *name); +void i3c_set_target_address(I3CTarget *dev, uint8_t address); +bool i3c_bus_busy(I3CBus *bus); + +/* + * Start a transfer on an I3C bus. + * If is_recv is known at compile-time (i.e. a device will always be sending or + * will always be receiving at a certain point), prefer to use i3c_start_recv or + * i3c_start_send instead. + * + * Returns 0 on success, non-zero on an error. + */ +int i3c_start_transfer(I3CBus *bus, uint8_t address, bool is_recv); + +/* + * Start a receive transfer on an I3C bus. + * + * Returns 0 on success, non-zero on an error + */ +int i3c_start_recv(I3CBus *bus, uint8_t address); + +/* + * Start a send transfer on an I3C bus. + * + * Returns 0 on success, non-zero on an error + */ +int i3c_start_send(I3CBus *bus, uint8_t address); + +void i3c_end_transfer(I3CBus *bus); +void i3c_nack(I3CBus *bus); +int i3c_send_byte(I3CBus *bus, uint8_t data); +int i3c_send(I3CBus *bus, const uint8_t *data, uint32_t num_to_send, + uint32_t *num_sent); +/* + * I3C receives can only NACK on a CCC. The target should NACK a CCC it does not + * support. + */ +int i3c_recv_byte(I3CBus *bus, uint8_t *data); +int i3c_recv(I3CBus *bus, uint8_t *data, uint32_t num_to_read, + uint32_t *num_read); +bool i3c_scan_bus(I3CBus *bus, uint8_t address); +int i3c_do_entdaa(I3CBus *bus, uint8_t address, uint64_t *pid, uint8_t *bcr, + uint8_t *dcr); +int i3c_start_device_transfer(I3CTarget *dev, int send_length); +bool i3c_target_match(I3CBus *bus, I3CTarget *target, uint8_t address); +int i3c_target_send_ibi(I3CTarget *t, uint8_t addr, bool is_recv); +int i3c_target_send_ibi_bytes(I3CTarget *t, uint8_t data); +int i3c_target_ibi_finish(I3CTarget *t, uint8_t data); + +/* + * Legacy I2C functions. + * + * These are wrapper for I2C functions that take in an I3C bus instead of an I2C + * bus. Internally they use the I2C bus (and devices attached to it) that's a + * part of the I3C bus + */ +void legacy_i2c_nack(I3CBus *bus); +uint8_t legacy_i2c_recv(I3CBus *bus); +int legacy_i2c_send(I3CBus *bus, uint8_t data); +int legacy_i2c_start_transfer(I3CBus *bus, uint8_t address, bool is_recv); +int legacy_i2c_start_recv(I3CBus *bus, uint8_t address); +int legacy_i2c_start_send(I3CBus *bus, uint8_t address); +void legacy_i2c_end_transfer(I3CBus *bus); +I2CSlave *legacy_i2c_device_create_simple(I3CBus *bus, const char *name, + uint8_t addr); + +/** + * Create an I3C Target. + * + * The target returned from this function still needs to be realized. + */ +I3CTarget *i3c_target_new(const char *name, uint8_t addr, uint8_t dcr, + uint8_t bcr, uint64_t pid); + +/** + * Create and realize an I3C target. + * + * Create the target, initialize it, put it on the specified I3C bus, and + * realize it. + */ +I3CTarget *i3c_target_create_simple(I3CBus *bus, const char *name, + uint8_t addr, uint8_t dcr, uint8_t bcr, + uint64_t pid); + +/* Realize and drop the reference count on an I3C target. */ +bool i3c_target_realize_and_unref(I3CTarget *dev, I3CBus *bus, Error **errp); + +#endif /* QEMU_INCLUDE_HW_I3C_I3C_H_ */ From patchwork Fri Mar 31 01:01:18 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Joe Komlodi X-Patchwork-Id: 1763549 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=nongnu.org (client-ip=209.51.188.17; helo=lists.gnu.org; envelope-from=qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org; receiver=) Authentication-Results: legolas.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=google.com header.i=@google.com header.a=rsa-sha256 header.s=20210112 header.b=n/MuMUdS; dkim-atps=neutral Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-ECDSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4PnhpP3r8Nz1yYr for ; Fri, 31 Mar 2023 12:02:21 +1100 (AEDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1pi39F-0003C7-UM; Thu, 30 Mar 2023 21:01:45 -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 <38jAmZAcKChU59769y319916z.x97Bz7F-yzGz689818F.9C1@flex--komlodi.bounces.google.com>) id 1pi39D-0003BG-U5 for qemu-devel@nongnu.org; Thu, 30 Mar 2023 21:01:43 -0400 Received: from mail-pl1-x649.google.com ([2607:f8b0:4864:20::649]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from <38jAmZAcKChU59769y319916z.x97Bz7F-yzGz689818F.9C1@flex--komlodi.bounces.google.com>) id 1pi39A-0006a4-Vx for qemu-devel@nongnu.org; Thu, 30 Mar 2023 21:01:42 -0400 Received: by mail-pl1-x649.google.com with SMTP id kw3-20020a170902f90300b001a274ccf620so3418151plb.8 for ; Thu, 30 Mar 2023 18:01:39 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20210112; t=1680224498; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=W+83e2X8nmqbmp/dPoWAi4L1ZZKNh/hKp43ZTipBcnk=; b=n/MuMUdSwIhZqi8OKNfVwTpyc58h8oIDaW/LOme0Uf4nANCgtyHzhEWAZUwkdpchcS vb152yK4+UE+/cuva63kTsV11wqsWzjOPSp2oq0l1MIEeeMm3y5y2BKTS90hvQhko8A5 xr5NQaR7xJndKx194fXuEf5R+rUZCRdu4fxcAFnXcQMM281vrDUNtpZLfg7IKFqxbM7M pOOsGAiu5x20PL8fKKUUdJFZFH/Ie19Bt0x37lVfhuQ/DtC9ECsyBuuOPec4jeb7jY2O f5/6DuTIFP2oPyHRO5VHdJ7omQMeEDY2bA3/7+iNETDTgpDNetU3Gi4rRFJM5QIx+J2H zqFw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; t=1680224498; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=W+83e2X8nmqbmp/dPoWAi4L1ZZKNh/hKp43ZTipBcnk=; b=xVeBbZPkn9NEOHB10EWQXFXJ0FszbCsq1pM9ETcjsmP+LLc0yv3xX5Fwc5zdDGLq21 5qvi6+7ZGgksNDb3mQH/v66R1DAqZn1nBTfImwmxafUCrnCIWMb9KgNTpM3l2syg5s8d gjmq9Hl8EKGf097bPMvb3yDPZI+N1CBxzdy8//vr+Ox/K5MV/N1fDJ47g2DsWOvXKf7v idaDtEpVnFMg7IpCqcN6DeflnYfHZh3pOaN+VJOnwA+Fakn0g+HjxWMXOZzVfu2UVY4f Nc5XIB8E6SZ0zYKiykW6VQMWJvy2XZaT+nFeruUn/hUVPQM6dYNBWKfr4jTk5w6d73H+ Z1xA== X-Gm-Message-State: AAQBX9dZ47ZVXm7wo2jmAYs7ZcvP08JJUBzCgVO72AkDEayLN6DETD5o rl0EdUtEavnsudhLM6HFqDrEK+XKe0nWXXwR/i/7H/jplFQy6gr28Y2ON/zYqjMLvtEPsrkmer7 SnSH/HK/X8kcKC3OzMPV4NiXr0tY7LgqhIVBGXuJ6X753zlP6G4tMXda0+TA6E3s= X-Google-Smtp-Source: AKy350ZJ8Nx73Xg/qgmf8CbS+jW0J1leSk4+adSOg1J3YoaXgcqgpP3LcUzxJswhjVxIQ5weVX3/ZWI2Dc0o X-Received: from komlodi.c.googlers.com ([fda3:e722:ac3:cc00:7f:e700:c0a8:35ee]) (user=komlodi job=sendgmr) by 2002:a17:903:2282:b0:1a0:428c:1dae with SMTP id b2-20020a170903228200b001a0428c1daemr9737193plh.5.1680224498553; Thu, 30 Mar 2023 18:01:38 -0700 (PDT) Date: Fri, 31 Mar 2023 01:01:18 +0000 In-Reply-To: <20230331010131.1412571-1-komlodi@google.com> Mime-Version: 1.0 References: <20230331010131.1412571-1-komlodi@google.com> X-Mailer: git-send-email 2.40.0.348.gf938b09366-goog Message-ID: <20230331010131.1412571-4-komlodi@google.com> Subject: [PATCH 03/16] hw/i3c/aspeed_i3c: Add more register fields From: Joe Komlodi To: qemu-devel@nongnu.org Cc: venture@google.com, komlodi@google.com, peter.maydell@linaro.org Received-SPF: pass client-ip=2607:f8b0:4864:20::649; envelope-from=38jAmZAcKChU59769y319916z.x97Bz7F-yzGz689818F.9C1@flex--komlodi.bounces.google.com; helo=mail-pl1-x649.google.com X-Spam_score_int: -95 X-Spam_score: -9.6 X-Spam_bar: --------- X-Spam_report: (-9.6 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_MED=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, UPPERCASE_75_100=0.001, USER_IN_DEF_DKIM_WL=-7.5 autolearn=no 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+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Adds the rest of the fields laid out in the AST26xx datasheet. Signed-off-by: Joe Komlodi Reviewed-by: Patrick Venture --- hw/i3c/aspeed_i3c.c | 260 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 248 insertions(+), 12 deletions(-) diff --git a/hw/i3c/aspeed_i3c.c b/hw/i3c/aspeed_i3c.c index 999978fb7d..ae247e03bb 100644 --- a/hw/i3c/aspeed_i3c.c +++ b/hw/i3c/aspeed_i3c.c @@ -2,6 +2,7 @@ * ASPEED I3C Controller * * Copyright (C) 2021 ASPEED Technology Inc. + * Copyright (C) 2023 Google LLC * * This code is licensed under the GPL version 2 or later. See * the COPYING file in the top-level directory. @@ -20,79 +21,314 @@ /* I3C Controller Registers */ REG32(I3C1_REG0, 0x10) REG32(I3C1_REG1, 0x14) - FIELD(I3C1_REG1, I2C_MODE, 0, 1) - FIELD(I3C1_REG1, SA_EN, 15, 1) + FIELD(I3C1_REG1, I2C_MODE, 0, 1) + FIELD(I3C1_REG1, SLV_TEST_MODE, 1, 1) + FIELD(I3C1_REG1, ACT_MODE, 2, 2) + FIELD(I3C1_REG1, PENDING_INT, 4, 4) + FIELD(I3C1_REG1, SA, 8, 7) + FIELD(I3C1_REG1, SA_EN, 15, 1) + FIELD(I3C1_REG1, INST_ID, 16, 4) REG32(I3C2_REG0, 0x20) REG32(I3C2_REG1, 0x24) - FIELD(I3C2_REG1, I2C_MODE, 0, 1) - FIELD(I3C2_REG1, SA_EN, 15, 1) + FIELD(I3C2_REG1, I2C_MODE, 0, 1) + FIELD(I3C2_REG1, SLV_TEST_MODE, 1, 1) + FIELD(I3C2_REG1, ACT_MODE, 2, 2) + FIELD(I3C2_REG1, PENDING_INT, 4, 4) + FIELD(I3C2_REG1, SA, 8, 7) + FIELD(I3C2_REG1, SA_EN, 15, 1) + FIELD(I3C2_REG1, INST_ID, 16, 4) REG32(I3C3_REG0, 0x30) REG32(I3C3_REG1, 0x34) - FIELD(I3C3_REG1, I2C_MODE, 0, 1) - FIELD(I3C3_REG1, SA_EN, 15, 1) + FIELD(I3C3_REG1, I2C_MODE, 0, 1) + FIELD(I3C3_REG1, SLV_TEST_MODE, 1, 1) + FIELD(I3C3_REG1, ACT_MODE, 2, 2) + FIELD(I3C3_REG1, PENDING_INT, 4, 4) + FIELD(I3C3_REG1, SA, 8, 7) + FIELD(I3C3_REG1, SA_EN, 15, 1) + FIELD(I3C3_REG1, INST_ID, 16, 4) REG32(I3C4_REG0, 0x40) REG32(I3C4_REG1, 0x44) - FIELD(I3C4_REG1, I2C_MODE, 0, 1) - FIELD(I3C4_REG1, SA_EN, 15, 1) + FIELD(I3C4_REG1, I2C_MODE, 0, 1) + FIELD(I3C4_REG1, SLV_TEST_MODE, 1, 1) + FIELD(I3C4_REG1, ACT_MODE, 2, 2) + FIELD(I3C4_REG1, PENDING_INT, 4, 4) + FIELD(I3C4_REG1, SA, 8, 7) + FIELD(I3C4_REG1, SA_EN, 15, 1) + FIELD(I3C4_REG1, INST_ID, 16, 4) REG32(I3C5_REG0, 0x50) REG32(I3C5_REG1, 0x54) - FIELD(I3C5_REG1, I2C_MODE, 0, 1) - FIELD(I3C5_REG1, SA_EN, 15, 1) + FIELD(I3C5_REG1, I2C_MODE, 0, 1) + FIELD(I3C5_REG1, SLV_TEST_MODE, 1, 1) + FIELD(I3C5_REG1, ACT_MODE, 2, 2) + FIELD(I3C5_REG1, PENDING_INT, 4, 4) + FIELD(I3C5_REG1, SA, 8, 7) + FIELD(I3C5_REG1, SA_EN, 15, 1) + FIELD(I3C5_REG1, INST_ID, 16, 4) REG32(I3C6_REG0, 0x60) REG32(I3C6_REG1, 0x64) - FIELD(I3C6_REG1, I2C_MODE, 0, 1) - FIELD(I3C6_REG1, SA_EN, 15, 1) + FIELD(I3C6_REG1, I2C_MODE, 0, 1) + FIELD(I3C6_REG1, SLV_TEST_MODE, 1, 1) + FIELD(I3C6_REG1, ACT_MODE, 2, 2) + FIELD(I3C6_REG1, PENDING_INT, 4, 4) + FIELD(I3C6_REG1, SA, 8, 7) + FIELD(I3C6_REG1, SA_EN, 15, 1) + FIELD(I3C6_REG1, INST_ID, 16, 4) /* I3C Device Registers */ REG32(DEVICE_CTRL, 0x00) + FIELD(DEVICE_CTRL, I3C_BROADCAST_ADDR_INC, 0, 1) + FIELD(DEVICE_CTRL, I2C_SLAVE_PRESENT, 7, 1) + FIELD(DEVICE_CTRL, HOT_JOIN_ACK_NACK_CTRL, 8, 1) + FIELD(DEVICE_CTRL, IDLE_CNT_MULTIPLIER, 24, 2) + FIELD(DEVICE_CTRL, SLV_ADAPT_TO_I2C_I3C_MODE, 27, 1) + FIELD(DEVICE_CTRL, DMA_HANDSHAKE_EN, 28, 1) + FIELD(DEVICE_CTRL, I3C_ABORT, 29, 1) + FIELD(DEVICE_CTRL, I3C_RESUME, 30, 1) + FIELD(DEVICE_CTRL, I3C_EN, 31, 1) REG32(DEVICE_ADDR, 0x04) + FIELD(DEVICE_ADDR, STATIC_ADDR, 0, 7) + FIELD(DEVICE_ADDR, STATIC_ADDR_VALID, 15, 1) + FIELD(DEVICE_ADDR, DYNAMIC_ADDR, 16, 7) + FIELD(DEVICE_ADDR, DYNAMIC_ADDR_VALID, 15, 1) REG32(HW_CAPABILITY, 0x08) + FIELD(HW_CAPABILITY, ENTDAA, 0, 1) + FIELD(HW_CAPABILITY, HDR_DDR, 3, 1) + FIELD(HW_CAPABILITY, HDR_TS, 4, 1) REG32(COMMAND_QUEUE_PORT, 0x0c) + FIELD(COMMAND_QUEUE_PORT, CMD_ATTR, 0, 3) + /* Transfer command structure */ + FIELD(COMMAND_QUEUE_PORT, TID, 3, 4) + FIELD(COMMAND_QUEUE_PORT, CMD, 7, 8) + FIELD(COMMAND_QUEUE_PORT, CP, 15, 1) + FIELD(COMMAND_QUEUE_PORT, DEV_INDEX, 16, 5) + FIELD(COMMAND_QUEUE_PORT, SPEED, 21, 3) + FIELD(COMMAND_QUEUE_PORT, ROC, 26, 1) + FIELD(COMMAND_QUEUE_PORT, SDAP, 27, 1) + FIELD(COMMAND_QUEUE_PORT, RNW, 28, 1) + FIELD(COMMAND_QUEUE_PORT, TOC, 30, 1) + FIELD(COMMAND_QUEUE_PORT, PEC, 31, 1) + /* Transfer argument data structure */ + FIELD(COMMAND_QUEUE_PORT, DB, 8, 8) + FIELD(COMMAND_QUEUE_PORT, DL, 16, 16) + /* Short data argument data structure */ + FIELD(COMMAND_QUEUE_PORT, BYTE_STRB, 3, 3) + FIELD(COMMAND_QUEUE_PORT, BYTE0, 8, 8) + FIELD(COMMAND_QUEUE_PORT, BYTE1, 16, 8) + FIELD(COMMAND_QUEUE_PORT, BYTE2, 24, 8) + /* Address assignment command structure */ + /* + * bits 3..21 and 26..31 are the same as the transfer command structure, or + * marked as reserved. + */ + FIELD(COMMAND_QUEUE_PORT, DEV_COUNT, 21, 3) REG32(RESPONSE_QUEUE_PORT, 0x10) + FIELD(RESPONSE_QUEUE_PORT, DL, 0, 16) + FIELD(RESPONSE_QUEUE_PORT, CCCT, 16, 8) + FIELD(RESPONSE_QUEUE_PORT, TID, 24, 4) + FIELD(RESPONSE_QUEUE_PORT, ERR_STATUS, 28, 4) REG32(RX_TX_DATA_PORT, 0x14) REG32(IBI_QUEUE_STATUS, 0x18) + FIELD(IBI_QUEUE_STATUS, IBI_DATA_LEN, 0, 8) + FIELD(IBI_QUEUE_STATUS, IBI_ID, 8, 8) + FIELD(IBI_QUEUE_STATUS, LAST_STATUS, 24, 1) + FIELD(IBI_QUEUE_STATUS, ERROR, 30, 1) + FIELD(IBI_QUEUE_STATUS, IBI_STATUS, 31, 1) REG32(IBI_QUEUE_DATA, 0x18) REG32(QUEUE_THLD_CTRL, 0x1c) + FIELD(QUEUE_THLD_CTRL, CMD_BUF_EMPTY_THLD, 0, 8); + FIELD(QUEUE_THLD_CTRL, RESP_BUF_THLD, 8, 8); + FIELD(QUEUE_THLD_CTRL, IBI_DATA_THLD, 16, 8); + FIELD(QUEUE_THLD_CTRL, IBI_STATUS_THLD, 24, 8); REG32(DATA_BUFFER_THLD_CTRL, 0x20) + FIELD(DATA_BUFFER_THLD_CTRL, TX_BUF_THLD, 0, 3) + FIELD(DATA_BUFFER_THLD_CTRL, RX_BUF_THLD, 10, 3) + FIELD(DATA_BUFFER_THLD_CTRL, TX_START_THLD, 16, 3) + FIELD(DATA_BUFFER_THLD_CTRL, RX_START_THLD, 24, 3) REG32(IBI_QUEUE_CTRL, 0x24) + FIELD(IBI_QUEUE_CTRL, NOTIFY_REJECTED_HOT_JOIN, 0, 1) + FIELD(IBI_QUEUE_CTRL, NOTIFY_REJECTED_MASTER_REQ, 1, 1) + FIELD(IBI_QUEUE_CTRL, NOTIFY_REJECTED_SLAVE_IRQ, 3, 1) REG32(IBI_MR_REQ_REJECT, 0x2c) REG32(IBI_SIR_REQ_REJECT, 0x30) REG32(RESET_CTRL, 0x34) + FIELD(RESET_CTRL, CORE_RESET, 0, 1) + FIELD(RESET_CTRL, CMD_QUEUE_RESET, 1, 1) + FIELD(RESET_CTRL, RESP_QUEUE_RESET, 2, 1) + FIELD(RESET_CTRL, TX_BUF_RESET, 3, 1) + FIELD(RESET_CTRL, RX_BUF_RESET, 4, 1) + FIELD(RESET_CTRL, IBI_QUEUE_RESET, 5, 1) REG32(SLV_EVENT_CTRL, 0x38) + FIELD(SLV_EVENT_CTRL, SLV_INTERRUPT, 0, 1) + FIELD(SLV_EVENT_CTRL, MASTER_INTERRUPT, 1, 1) + FIELD(SLV_EVENT_CTRL, HOT_JOIN_INTERRUPT, 3, 1) + FIELD(SLV_EVENT_CTRL, ACTIVITY_STATE, 4, 2) + FIELD(SLV_EVENT_CTRL, MRL_UPDATED, 6, 1) + FIELD(SLV_EVENT_CTRL, MWL_UPDATED, 7, 1) REG32(INTR_STATUS, 0x3c) + FIELD(INTR_STATUS, TX_THLD, 0, 1) + FIELD(INTR_STATUS, RX_THLD, 1, 1) + FIELD(INTR_STATUS, IBI_THLD, 2, 1) + FIELD(INTR_STATUS, CMD_QUEUE_RDY, 3, 1) + FIELD(INTR_STATUS, RESP_RDY, 4, 1) + FIELD(INTR_STATUS, TRANSFER_ABORT, 5, 1) + FIELD(INTR_STATUS, CCC_UPDATED, 6, 1) + FIELD(INTR_STATUS, DYN_ADDR_ASSGN, 8, 1) + FIELD(INTR_STATUS, TRANSFER_ERR, 9, 1) + FIELD(INTR_STATUS, DEFSLV, 10, 1) + FIELD(INTR_STATUS, READ_REQ_RECV, 11, 1) + FIELD(INTR_STATUS, IBI_UPDATED, 12, 1) + FIELD(INTR_STATUS, BUSOWNER_UPDATED, 13, 1) REG32(INTR_STATUS_EN, 0x40) + FIELD(INTR_STATUS_EN, TX_THLD, 0, 1) + FIELD(INTR_STATUS_EN, RX_THLD, 1, 1) + FIELD(INTR_STATUS_EN, IBI_THLD, 2, 1) + FIELD(INTR_STATUS_EN, CMD_QUEUE_RDY, 3, 1) + FIELD(INTR_STATUS_EN, RESP_RDY, 4, 1) + FIELD(INTR_STATUS_EN, TRANSFER_ABORT, 5, 1) + FIELD(INTR_STATUS_EN, CCC_UPDATED, 6, 1) + FIELD(INTR_STATUS_EN, DYN_ADDR_ASSGN, 8, 1) + FIELD(INTR_STATUS_EN, TRANSFER_ERR, 9, 1) + FIELD(INTR_STATUS_EN, DEFSLV, 10, 1) + FIELD(INTR_STATUS_EN, READ_REQ_RECV, 11, 1) + FIELD(INTR_STATUS_EN, IBI_UPDATED, 12, 1) + FIELD(INTR_STATUS_EN, BUSOWNER_UPDATED, 13, 1) REG32(INTR_SIGNAL_EN, 0x44) + FIELD(INTR_SIGNAL_EN, TX_THLD, 0, 1) + FIELD(INTR_SIGNAL_EN, RX_THLD, 1, 1) + FIELD(INTR_SIGNAL_EN, IBI_THLD, 2, 1) + FIELD(INTR_SIGNAL_EN, CMD_QUEUE_RDY, 3, 1) + FIELD(INTR_SIGNAL_EN, RESP_RDY, 4, 1) + FIELD(INTR_SIGNAL_EN, TRANSFER_ABORT, 5, 1) + FIELD(INTR_SIGNAL_EN, CCC_UPDATED, 6, 1) + FIELD(INTR_SIGNAL_EN, DYN_ADDR_ASSGN, 8, 1) + FIELD(INTR_SIGNAL_EN, TRANSFER_ERR, 9, 1) + FIELD(INTR_SIGNAL_EN, DEFSLV, 10, 1) + FIELD(INTR_SIGNAL_EN, READ_REQ_RECV, 11, 1) + FIELD(INTR_SIGNAL_EN, IBI_UPDATED, 12, 1) + FIELD(INTR_SIGNAL_EN, BUSOWNER_UPDATED, 13, 1) REG32(INTR_FORCE, 0x48) + FIELD(INTR_FORCE, TX_THLD, 0, 1) + FIELD(INTR_FORCE, RX_THLD, 1, 1) + FIELD(INTR_FORCE, IBI_THLD, 2, 1) + FIELD(INTR_FORCE, CMD_QUEUE_RDY, 3, 1) + FIELD(INTR_FORCE, RESP_RDY, 4, 1) + FIELD(INTR_FORCE, TRANSFER_ABORT, 5, 1) + FIELD(INTR_FORCE, CCC_UPDATED, 6, 1) + FIELD(INTR_FORCE, DYN_ADDR_ASSGN, 8, 1) + FIELD(INTR_FORCE, TRANSFER_ERR, 9, 1) + FIELD(INTR_FORCE, DEFSLV, 10, 1) + FIELD(INTR_FORCE, READ_REQ_RECV, 11, 1) + FIELD(INTR_FORCE, IBI_UPDATED, 12, 1) + FIELD(INTR_FORCE, BUSOWNER_UPDATED, 13, 1) REG32(QUEUE_STATUS_LEVEL, 0x4c) + FIELD(QUEUE_STATUS_LEVEL, CMD_QUEUE_EMPTY_LOC, 0, 8) + FIELD(QUEUE_STATUS_LEVEL, RESP_BUF_BLR, 8, 8) + FIELD(QUEUE_STATUS_LEVEL, IBI_BUF_BLR, 16, 8) + FIELD(QUEUE_STATUS_LEVEL, IBI_STATUS_CNT, 24, 5) REG32(DATA_BUFFER_STATUS_LEVEL, 0x50) + FIELD(DATA_BUFFER_STATUS_LEVEL, TX_BUF_EMPTY_LOC, 0, 8) + FIELD(DATA_BUFFER_STATUS_LEVEL, RX_BUF_BLR, 16, 8) REG32(PRESENT_STATE, 0x54) + FIELD(PRESENT_STATE, SCL_LINE_SIGNAL_LEVEL, 0, 1) + FIELD(PRESENT_STATE, SDA_LINE_SIGNAL_LEVEL, 1, 1) + FIELD(PRESENT_STATE, CURRENT_MASTER, 2, 1) + FIELD(PRESENT_STATE, CM_TFR_STATUS, 8, 6) + FIELD(PRESENT_STATE, CM_TFR_ST_STATUS, 16, 6) + FIELD(PRESENT_STATE, CMD_TID, 24, 4) REG32(CCC_DEVICE_STATUS, 0x58) + FIELD(CCC_DEVICE_STATUS, PENDING_INTR, 0, 4) + FIELD(CCC_DEVICE_STATUS, PROTOCOL_ERR, 4, 2) + FIELD(CCC_DEVICE_STATUS, ACTIVITY_MODE, 6, 2) + FIELD(CCC_DEVICE_STATUS, UNDER_ERR, 8, 1) + FIELD(CCC_DEVICE_STATUS, SLV_BUSY, 9, 1) + FIELD(CCC_DEVICE_STATUS, OVERFLOW_ERR, 10, 1) + FIELD(CCC_DEVICE_STATUS, DATA_NOT_READY, 11, 1) + FIELD(CCC_DEVICE_STATUS, BUFFER_NOT_AVAIL, 12, 1) REG32(DEVICE_ADDR_TABLE_POINTER, 0x5c) FIELD(DEVICE_ADDR_TABLE_POINTER, DEPTH, 16, 16) FIELD(DEVICE_ADDR_TABLE_POINTER, ADDR, 0, 16) REG32(DEV_CHAR_TABLE_POINTER, 0x60) + FIELD(DEV_CHAR_TABLE_POINTER, P_DEV_CHAR_TABLE_START_ADDR, 0, 12) + FIELD(DEV_CHAR_TABLE_POINTER, DEV_CHAR_TABLE_DEPTH, 12, 7) + FIELD(DEV_CHAR_TABLE_POINTER, PRESENT_DEV_CHAR_TABLE_INDEX, 19, 3) REG32(VENDOR_SPECIFIC_REG_POINTER, 0x6c) + FIELD(VENDOR_SPECIFIC_REG_POINTER, P_VENDOR_REG_START_ADDR, 0, 16) REG32(SLV_MIPI_PID_VALUE, 0x70) REG32(SLV_PID_VALUE, 0x74) + FIELD(SLV_PID_VALUE, SLV_PID_DCR, 0, 12) + FIELD(SLV_PID_VALUE, SLV_INST_ID, 12, 4) + FIELD(SLV_PID_VALUE, SLV_PART_ID, 16, 16) REG32(SLV_CHAR_CTRL, 0x78) + FIELD(SLV_CHAR_CTRL, BCR, 0, 8) + FIELD(SLV_CHAR_CTRL, DCR, 8, 8) + FIELD(SLV_CHAR_CTRL, HDR_CAP, 16, 8) REG32(SLV_MAX_LEN, 0x7c) + FIELD(SLV_MAX_LEN, MWL, 0, 16) + FIELD(SLV_MAX_LEN, MRL, 16, 16) REG32(MAX_READ_TURNAROUND, 0x80) REG32(MAX_DATA_SPEED, 0x84) REG32(SLV_DEBUG_STATUS, 0x88) REG32(SLV_INTR_REQ, 0x8c) + FIELD(SLV_INTR_REQ, SIR, 0, 1) + FIELD(SLV_INTR_REQ, SIR_CTRL, 1, 2) + FIELD(SLV_INTR_REQ, MIR, 3, 1) + FIELD(SLV_INTR_REQ, IBI_STS, 8, 2) +REG32(SLV_TSX_SYMBL_TIMING, 0x90) + FIELD(SLV_TSX_SYMBL_TIMING, SLV_TSX_SYMBL_CNT, 0, 6) REG32(DEVICE_CTRL_EXTENDED, 0xb0) + FIELD(DEVICE_CTRL_EXTENDED, MODE, 0, 2) + FIELD(DEVICE_CTRL_EXTENDED, REQMST_ACK_CTRL, 3, 1) REG32(SCL_I3C_OD_TIMING, 0xb4) + FIELD(SCL_I3C_OD_TIMING, I3C_OD_LCNT, 0, 8) + FIELD(SCL_I3C_OD_TIMING, I3C_OD_HCNT, 16, 8) REG32(SCL_I3C_PP_TIMING, 0xb8) + FIELD(SCL_I3C_PP_TIMING, I3C_PP_LCNT, 0, 8) + FIELD(SCL_I3C_PP_TIMING, I3C_PP_HCNT, 16, 8) REG32(SCL_I2C_FM_TIMING, 0xbc) REG32(SCL_I2C_FMP_TIMING, 0xc0) + FIELD(SCL_I2C_FMP_TIMING, I2C_FMP_LCNT, 0, 16) + FIELD(SCL_I2C_FMP_TIMING, I2C_FMP_HCNT, 16, 8) REG32(SCL_EXT_LCNT_TIMING, 0xc8) REG32(SCL_EXT_TERMN_LCNT_TIMING, 0xcc) REG32(BUS_FREE_TIMING, 0xd4) REG32(BUS_IDLE_TIMING, 0xd8) + FIELD(BUS_IDLE_TIMING, BUS_IDLE_TIME, 0, 20) REG32(I3C_VER_ID, 0xe0) REG32(I3C_VER_TYPE, 0xe4) REG32(EXTENDED_CAPABILITY, 0xe8) + FIELD(EXTENDED_CAPABILITY, APP_IF_MODE, 0, 2) + FIELD(EXTENDED_CAPABILITY, APP_IF_DATA_WIDTH, 2, 2) + FIELD(EXTENDED_CAPABILITY, OPERATION_MODE, 4, 2) + FIELD(EXTENDED_CAPABILITY, CLK_PERIOD, 8, 6) REG32(SLAVE_CONFIG, 0xec) + FIELD(SLAVE_CONFIG, DMA_EN, 0, 1) + FIELD(SLAVE_CONFIG, HJ_CAP, 0, 1) + FIELD(SLAVE_CONFIG, CLK_PERIOD, 2, 14) +/* Device characteristic table fields */ +REG32(DEVICE_CHARACTERISTIC_TABLE_LOC1, 0x200) +REG32(DEVICE_CHARACTERISTIC_TABLE_LOC_SECONDARY, 0x200) + FIELD(DEVICE_CHARACTERISTIC_TABLE_LOC_SECONDARY, DYNAMIC_ADDR, 0, 8) + FIELD(DEVICE_CHARACTERISTIC_TABLE_LOC_SECONDARY, DCR, 8, 8) + FIELD(DEVICE_CHARACTERISTIC_TABLE_LOC_SECONDARY, BCR, 16, 8) + FIELD(DEVICE_CHARACTERISTIC_TABLE_LOC_SECONDARY, STATIC_ADDR, 24, 8) +REG32(DEVICE_CHARACTERISTIC_TABLE_LOC2, 0x204) + FIELD(DEVICE_CHARACTERISTIC_TABLE_LOC2, MSB_PID, 0, 16) +REG32(DEVICE_CHARACTERISTIC_TABLE_LOC3, 0x208) + FIELD(DEVICE_CHARACTERISTIC_TABLE_LOC3, DCR, 0, 8) + FIELD(DEVICE_CHARACTERISTIC_TABLE_LOC3, BCR, 8, 8) +REG32(DEVICE_CHARACTERISTIC_TABLE_LOC4, 0x20c) + FIELD(DEVICE_CHARACTERISTIC_TABLE_LOC4, DEV_DYNAMIC_ADDR, 0, 8) +/* Dev addr table fields */ +REG32(DEVICE_ADDR_TABLE_LOC1, 0x280) + FIELD(DEVICE_ADDR_TABLE_LOC1, DEV_STATIC_ADDR, 0, 7) + FIELD(DEVICE_ADDR_TABLE_LOC1, IBI_PEC_EN, 11, 1) + FIELD(DEVICE_ADDR_TABLE_LOC1, IBI_WITH_DATA, 12, 1) + FIELD(DEVICE_ADDR_TABLE_LOC1, SIR_REJECT, 13, 1) + FIELD(DEVICE_ADDR_TABLE_LOC1, MR_REJECT, 14, 1) + FIELD(DEVICE_ADDR_TABLE_LOC1, DEV_DYNAMIC_ADDR, 16, 8) + FIELD(DEVICE_ADDR_TABLE_LOC1, IBI_ADDR_MASK, 24, 2) + FIELD(DEVICE_ADDR_TABLE_LOC1, DEV_NACK_RETRY_CNT, 29, 2) + FIELD(DEVICE_ADDR_TABLE_LOC1, LEGACY_I2C_DEVICE, 31, 1) static const uint32_t ast2600_i3c_device_resets[ASPEED_I3C_DEVICE_NR_REGS] = { [R_HW_CAPABILITY] = 0x000e00bf, From patchwork Fri Mar 31 01:01:19 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Joe Komlodi X-Patchwork-Id: 1763548 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=nongnu.org (client-ip=209.51.188.17; helo=lists.gnu.org; envelope-from=qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org; receiver=) Authentication-Results: legolas.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=google.com header.i=@google.com header.a=rsa-sha256 header.s=20210112 header.b=pTogh7lD; dkim-atps=neutral Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-ECDSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4Pnhp75Pd0z1yZ0 for ; Fri, 31 Mar 2023 12:02:07 +1100 (AEDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1pi39H-0003Co-Ux; Thu, 30 Mar 2023 21:01:47 -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 <38zAmZAcKChY6A87Az42AA270.yA8C08G-z0H079A929G.AD2@flex--komlodi.bounces.google.com>) id 1pi39E-0003Bg-M1 for qemu-devel@nongnu.org; Thu, 30 Mar 2023 21:01:44 -0400 Received: from mail-pl1-x649.google.com ([2607:f8b0:4864:20::649]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from <38zAmZAcKChY6A87Az42AA270.yA8C08G-z0H079A929G.AD2@flex--komlodi.bounces.google.com>) id 1pi39B-0006aI-PW for qemu-devel@nongnu.org; Thu, 30 Mar 2023 21:01:44 -0400 Received: by mail-pl1-x649.google.com with SMTP id a9-20020a170902b58900b0019e2eafafddso11965413pls.7 for ; Thu, 30 Mar 2023 18:01:41 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20210112; t=1680224500; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=XOXRBZo6SUoSd57AynvNd+UVB2okpfDqLUFbHhxgplU=; b=pTogh7lDs/GEKRGo3to58z1Sxw3OD8mhx7g3RSwhV0K2yrKtMNDF4MyZIjKjoS9JaU l2KBBFIAMvmDa1q+hcgL2ULE3wd99byyNQt5shw/pYmy2JhFVOwQG2IyN+yLaZM6Bx7k D0Jr7TM+Ms3Ov1thm5nUo4NsUhAKDa1rLc7oNqyT0KETgwWNeVXc8X/CVbZIfvYeITqx 5NHdxx8fe4VmIBNdYy37ibeGmhZoih+1IhhwlFa3AGPLULtgaL19jrYAA7ZaEG2igjrA u4QMhp629Y8dKd+TzAi9X40hpxeblaVrpWKcnA8cC/IFfKwHXXZorll6qvTkN+qMAPbq rIHg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; t=1680224500; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=XOXRBZo6SUoSd57AynvNd+UVB2okpfDqLUFbHhxgplU=; b=8B26z9vikTSM9x3r3v+A2rhbGQiangkZBwKlQ9qZG8jvETZo23TQR9DhwQs74ho8xF wuDFC2PoKRc8C9FfjQtjlWU0ZpbBd8i9L8pA/9AppnlMunFleCKof8aSDZgE6Xwza5jN AGSNLLyGjK+XO3msLmYqH9rZRd8ChdB9An34+iBSI4NH+fsYkVAcLVzB+r+xe4G1ONmp rVgplKfa9xESL96AGuAndiC0oBibuiI/o0Qxk9eVWKyo/n+mm+EoFsNtEqSUHATpZjwP odjps4nr2z7mVgpqaU8OANAQmSUPIIDhWcpO1H3Hy5yijNDqj6dLCk9fFGtqoEH6FNGd 5nDw== X-Gm-Message-State: AAQBX9cZayZBQtbkWVtFR+uhymgkVRa8RVHj/1k7OqfJrURFAoZhH9m0 NOVnVEVTABjf0bK8hpOz1hiGeDkNgBMKfg0MK/eTzppG0FGL7ooP1RE/aN/V3zwd0lDLyGs2rnY CWnM/Cu/+0D08wTi6IuPfr/NvfuU5V/xf8E8G6M1oCH5tqfR9O7ROhpYWA9OC6wk= X-Google-Smtp-Source: AKy350aFkDqEJs7zIUFN4xtt6o4T6UU2uynbcOi41xETdiSSjkMlgd1XWzmNZldeTiCf92sU9XlY489V5oJt X-Received: from komlodi.c.googlers.com ([fda3:e722:ac3:cc00:7f:e700:c0a8:35ee]) (user=komlodi job=sendgmr) by 2002:a63:a4a:0:b0:502:f20a:6e0a with SMTP id z10-20020a630a4a000000b00502f20a6e0amr2426079pgk.0.1680224499997; Thu, 30 Mar 2023 18:01:39 -0700 (PDT) Date: Fri, 31 Mar 2023 01:01:19 +0000 In-Reply-To: <20230331010131.1412571-1-komlodi@google.com> Mime-Version: 1.0 References: <20230331010131.1412571-1-komlodi@google.com> X-Mailer: git-send-email 2.40.0.348.gf938b09366-goog Message-ID: <20230331010131.1412571-5-komlodi@google.com> Subject: [PATCH 04/16] hw/i3c/aspeed_i3c: Add more reset values From: Joe Komlodi To: qemu-devel@nongnu.org Cc: venture@google.com, komlodi@google.com, peter.maydell@linaro.org Received-SPF: pass client-ip=2607:f8b0:4864:20::649; envelope-from=38zAmZAcKChY6A87Az42AA270.yA8C08G-z0H079A929G.AD2@flex--komlodi.bounces.google.com; helo=mail-pl1-x649.google.com X-Spam_score_int: -95 X-Spam_score: -9.6 X-Spam_bar: --------- X-Spam_report: (-9.6 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_MED=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, USER_IN_DEF_DKIM_WL=-7.5 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+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Adds reset values for the new registers added. Signed-off-by: Joe Komlodi Reviewed-by: Patrick Venture --- hw/i3c/aspeed_i3c.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/hw/i3c/aspeed_i3c.c b/hw/i3c/aspeed_i3c.c index ae247e03bb..034a17ff8e 100644 --- a/hw/i3c/aspeed_i3c.c +++ b/hw/i3c/aspeed_i3c.c @@ -333,12 +333,29 @@ REG32(DEVICE_ADDR_TABLE_LOC1, 0x280) static const uint32_t ast2600_i3c_device_resets[ASPEED_I3C_DEVICE_NR_REGS] = { [R_HW_CAPABILITY] = 0x000e00bf, [R_QUEUE_THLD_CTRL] = 0x01000101, + [R_DATA_BUFFER_THLD_CTRL] = 0x01010100, + [R_SLV_EVENT_CTRL] = 0x0000000b, + [R_QUEUE_STATUS_LEVEL] = 0x00000002, + [R_DATA_BUFFER_STATUS_LEVEL] = 0x00000010, + [R_PRESENT_STATE] = 0x00000003, [R_I3C_VER_ID] = 0x3130302a, [R_I3C_VER_TYPE] = 0x6c633033, [R_DEVICE_ADDR_TABLE_POINTER] = 0x00080280, [R_DEV_CHAR_TABLE_POINTER] = 0x00020200, + [R_SLV_CHAR_CTRL] = 0x00010000, [A_VENDOR_SPECIFIC_REG_POINTER] = 0x000000b0, [R_SLV_MAX_LEN] = 0x00ff00ff, + [R_SLV_TSX_SYMBL_TIMING] = 0x0000003f, + [R_SCL_I3C_OD_TIMING] = 0x000a0010, + [R_SCL_I3C_PP_TIMING] = 0x000a000a, + [R_SCL_I2C_FM_TIMING] = 0x00100010, + [R_SCL_I2C_FMP_TIMING] = 0x00100010, + [R_SCL_EXT_LCNT_TIMING] = 0x20202020, + [R_SCL_EXT_TERMN_LCNT_TIMING] = 0x00300000, + [R_BUS_FREE_TIMING] = 0x00200020, + [R_BUS_IDLE_TIMING] = 0x00000020, + [R_EXTENDED_CAPABILITY] = 0x00000239, + [R_SLAVE_CONFIG] = 0x00000023, }; static uint64_t aspeed_i3c_device_read(void *opaque, hwaddr offset, From patchwork Fri Mar 31 01:01:20 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Joe Komlodi X-Patchwork-Id: 1763561 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=nongnu.org (client-ip=209.51.188.17; helo=lists.gnu.org; envelope-from=qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org; receiver=) Authentication-Results: legolas.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=google.com header.i=@google.com header.a=rsa-sha256 header.s=20210112 header.b=Iy2Bt0+P; dkim-atps=neutral Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-ECDSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4Pnht11c4Bz1yY8 for ; Fri, 31 Mar 2023 12:05:29 +1100 (AEDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1pi39H-0003Cb-0G; Thu, 30 Mar 2023 21:01:47 -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 <39TAmZAcKChg8CA9C164CC492.0CAE2AI-12J29BCB4BI.CF4@flex--komlodi.bounces.google.com>) id 1pi39F-0003Bx-MT for qemu-devel@nongnu.org; Thu, 30 Mar 2023 21:01:45 -0400 Received: from mail-pj1-x1049.google.com ([2607:f8b0:4864:20::1049]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from <39TAmZAcKChg8CA9C164CC492.0CAE2AI-12J29BCB4BI.CF4@flex--komlodi.bounces.google.com>) id 1pi39D-0006aU-VG for qemu-devel@nongnu.org; Thu, 30 Mar 2023 21:01:45 -0400 Received: by mail-pj1-x1049.google.com with SMTP id ml17-20020a17090b361100b0023f9e99ab95so10021655pjb.1 for ; Thu, 30 Mar 2023 18:01:42 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20210112; t=1680224502; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=A+pyeFG3TXhnkPx8nHzV8fFrhPD3LijsaszOI3hOF4A=; b=Iy2Bt0+PA6boFbHploToxVXDGmLKxK9O/XC2pUGAp9R6+vrm4g0xvqVEfYZcESzk6O VD2dMdz25Hysrh8knEJZeAoqJrYhary9O1IMCyRydb5a7GzFrO6Eh9WpPWk1JCT2ZubI 6qy1cENWrqntPx+u7vCL0yRREI5PUA/eP3tbz9IpO1dHDcJy+XQeIK5HJWQKTr3GXNjx GDqVhXVp+2dvP6ZFDt5B0MVI6ctEsWETE0vk2N2EvsjHED8q7kojd1ZWdK0fdAQstE+g gbmhIbdOQBKnciSGQXgecBlWyi6eaTDN0a5fvjJpKki0ZR1CY9f5ix9SI6F9Gsf/Q5/L 8jKw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; t=1680224502; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=A+pyeFG3TXhnkPx8nHzV8fFrhPD3LijsaszOI3hOF4A=; b=wXxOVM883E9anVbHEsoHKL4OPzCExS3301FG6DMuFeIc0QaUynS2dDn4Yn8kokE7Eh 5u2RgMQTfu8lvHUTrBjcL0fjiv9qY6JqfZMM+u4r5Zli4d9hIqRiZ7fexJFTF4SHMd5X C/JUKCA/pkPSTy9r/zESPOn3Op4fkz1HurlrMx54P+YNGfKJYu3DoVB6b6/tDP4uKOsu VCTEZF0lDDXJxj1v4in+HTR0WgtZSKAprpLtyYDxsxDcoqV14+c7l/h79egz/kMbVFP2 bfnZSYc5iLKeCpWx81JffJg/qFo3YWm6ViL92hOtjl7dWabBR0ORlnrL4s4gaOoVT+Nb pkLw== X-Gm-Message-State: AAQBX9fpsUifeSY27TEWjq8i6VwTknpg/inCoeHX/BZGLWl9J8tTJa7t kEvnsZ/+raWVM/4bN9Q1VcXLLYrAzZxzZkhG+lND+h3OvjD9AGooCdfz6YoqHzjP0Q/v/ROycBc 7+nLvBVXc61H65etfwbZq3gl0xW6UNaleOvOxQsbTYAWxTJEpPihi5KOyrovcPHc= X-Google-Smtp-Source: AKy350b1wK8vEsr7hKIHFcSOoXRAG6VId6khwK2sjKwZVuUhTHNZ8npatQK3bO7jPq+ljjtd6TaISvKi3ilh X-Received: from komlodi.c.googlers.com ([fda3:e722:ac3:cc00:7f:e700:c0a8:35ee]) (user=komlodi job=sendgmr) by 2002:a17:903:234c:b0:19a:850a:d88b with SMTP id c12-20020a170903234c00b0019a850ad88bmr3038197plh.4.1680224501937; Thu, 30 Mar 2023 18:01:41 -0700 (PDT) Date: Fri, 31 Mar 2023 01:01:20 +0000 In-Reply-To: <20230331010131.1412571-1-komlodi@google.com> Mime-Version: 1.0 References: <20230331010131.1412571-1-komlodi@google.com> X-Mailer: git-send-email 2.40.0.348.gf938b09366-goog Message-ID: <20230331010131.1412571-6-komlodi@google.com> Subject: [PATCH 05/16] hw/i3c/aspeed_i3c: Add register RO field masks From: Joe Komlodi To: qemu-devel@nongnu.org Cc: venture@google.com, komlodi@google.com, peter.maydell@linaro.org Received-SPF: pass client-ip=2607:f8b0:4864:20::1049; envelope-from=39TAmZAcKChg8CA9C164CC492.0CAE2AI-12J29BCB4BI.CF4@flex--komlodi.bounces.google.com; helo=mail-pj1-x1049.google.com X-Spam_score_int: -95 X-Spam_score: -9.6 X-Spam_bar: --------- X-Spam_report: (-9.6 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_MED=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, USER_IN_DEF_DKIM_WL=-7.5 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+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Adds read-only field masks for the I3C device and controller registers. Signed-off-by: Joe Komlodi Reviewed-by: Patrick Venture --- hw/i3c/aspeed_i3c.c | 56 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/hw/i3c/aspeed_i3c.c b/hw/i3c/aspeed_i3c.c index 034a17ff8e..6f514bef5e 100644 --- a/hw/i3c/aspeed_i3c.c +++ b/hw/i3c/aspeed_i3c.c @@ -330,6 +330,21 @@ REG32(DEVICE_ADDR_TABLE_LOC1, 0x280) FIELD(DEVICE_ADDR_TABLE_LOC1, DEV_NACK_RETRY_CNT, 29, 2) FIELD(DEVICE_ADDR_TABLE_LOC1, LEGACY_I2C_DEVICE, 31, 1) +static const uint32_t ast2600_i3c_controller_ro[ASPEED_I3C_DEVICE_NR_REGS] = { + [R_I3C1_REG0] = 0xfc000000, + [R_I3C1_REG1] = 0xfff00000, + [R_I3C2_REG0] = 0xfc000000, + [R_I3C2_REG1] = 0xfff00000, + [R_I3C3_REG0] = 0xfc000000, + [R_I3C3_REG1] = 0xfff00000, + [R_I3C4_REG0] = 0xfc000000, + [R_I3C4_REG1] = 0xfff00000, + [R_I3C5_REG0] = 0xfc000000, + [R_I3C5_REG1] = 0xfff00000, + [R_I3C6_REG0] = 0xfc000000, + [R_I3C6_REG1] = 0xfff00000, +}; + static const uint32_t ast2600_i3c_device_resets[ASPEED_I3C_DEVICE_NR_REGS] = { [R_HW_CAPABILITY] = 0x000e00bf, [R_QUEUE_THLD_CTRL] = 0x01000101, @@ -358,6 +373,45 @@ static const uint32_t ast2600_i3c_device_resets[ASPEED_I3C_DEVICE_NR_REGS] = { [R_SLAVE_CONFIG] = 0x00000023, }; +static const uint32_t ast2600_i3c_device_ro[ASPEED_I3C_DEVICE_NR_REGS] = { + [R_DEVICE_CTRL] = 0x04fffe00, + [R_DEVICE_ADDR] = 0x7f807f80, + [R_HW_CAPABILITY] = 0xffffffff, + [R_IBI_QUEUE_STATUS] = 0xffffffff, + [R_DATA_BUFFER_THLD_CTRL] = 0xf8f8f8f8, + [R_IBI_QUEUE_CTRL] = 0xfffffff0, + [R_RESET_CTRL] = 0xffffffc0, + [R_SLV_EVENT_CTRL] = 0xffffff3f, + [R_INTR_STATUS] = 0xffff809f, + [R_INTR_STATUS_EN] = 0xffff8080, + [R_INTR_SIGNAL_EN] = 0xffff8080, + [R_INTR_FORCE] = 0xffff8000, + [R_QUEUE_STATUS_LEVEL] = 0xffffffff, + [R_DATA_BUFFER_STATUS_LEVEL] = 0xffffffff, + [R_PRESENT_STATE] = 0xffffffff, + [R_CCC_DEVICE_STATUS] = 0xffffffff, + [R_I3C_VER_ID] = 0xffffffff, + [R_I3C_VER_TYPE] = 0xffffffff, + [R_DEVICE_ADDR_TABLE_POINTER] = 0xffffffff, + [R_DEV_CHAR_TABLE_POINTER] = 0xffcbffff, + [R_SLV_PID_VALUE] = 0xffff0fff, + [R_SLV_CHAR_CTRL] = 0xffffffff, + [A_VENDOR_SPECIFIC_REG_POINTER] = 0xffffffff, + [R_SLV_MAX_LEN] = 0xffffffff, + [R_MAX_READ_TURNAROUND] = 0xffffffff, + [R_MAX_DATA_SPEED] = 0xffffffff, + [R_SLV_INTR_REQ] = 0xfffffff0, + [R_SLV_TSX_SYMBL_TIMING] = 0xffffffc0, + [R_DEVICE_CTRL_EXTENDED] = 0xfffffff8, + [R_SCL_I3C_OD_TIMING] = 0xff00ff00, + [R_SCL_I3C_PP_TIMING] = 0xff00ff00, + [R_SCL_I2C_FMP_TIMING] = 0xff000000, + [R_SCL_EXT_TERMN_LCNT_TIMING] = 0x0000fff0, + [R_BUS_IDLE_TIMING] = 0xfff00000, + [R_EXTENDED_CAPABILITY] = 0xffffffff, + [R_SLAVE_CONFIG] = 0xffffffff, +}; + static uint64_t aspeed_i3c_device_read(void *opaque, hwaddr offset, unsigned size) { @@ -387,6 +441,7 @@ static void aspeed_i3c_device_write(void *opaque, hwaddr offset, trace_aspeed_i3c_device_write(s->id, offset, value); + value &= ~ast2600_i3c_device_ro[addr]; switch (addr) { case R_HW_CAPABILITY: case R_RESPONSE_QUEUE_PORT: @@ -475,6 +530,7 @@ static void aspeed_i3c_write(void *opaque, addr >>= 2; + data &= ~ast2600_i3c_controller_ro[addr]; /* I3C controller register */ switch (addr) { case R_I3C1_REG1: From patchwork Fri Mar 31 01:01:21 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Joe Komlodi X-Patchwork-Id: 1763552 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=nongnu.org (client-ip=209.51.188.17; helo=lists.gnu.org; envelope-from=qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org; receiver=) Authentication-Results: legolas.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=google.com header.i=@google.com header.a=rsa-sha256 header.s=20210112 header.b=Qp9t8wQs; dkim-atps=neutral Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-ECDSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4PnhqK4yQKz1yYr for ; Fri, 31 Mar 2023 12:03:09 +1100 (AEDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1pi39J-0003DQ-8B; Thu, 30 Mar 2023 21:01:49 -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 <39zAmZAcKChoAECBE386EE6B4.2ECG4CK-34L4BDED6DK.EH6@flex--komlodi.bounces.google.com>) id 1pi39G-0003Ca-Cr for qemu-devel@nongnu.org; Thu, 30 Mar 2023 21:01:46 -0400 Received: from mail-pj1-x104a.google.com ([2607:f8b0:4864:20::104a]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from <39zAmZAcKChoAECBE386EE6B4.2ECG4CK-34L4BDED6DK.EH6@flex--komlodi.bounces.google.com>) id 1pi39E-0006ak-R8 for qemu-devel@nongnu.org; Thu, 30 Mar 2023 21:01:46 -0400 Received: by mail-pj1-x104a.google.com with SMTP id e8-20020a17090a118800b0023d35ae431eso6451063pja.8 for ; Thu, 30 Mar 2023 18:01:44 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20210112; t=1680224503; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=AZsj2vydlw7mk9QHWSB9x4ES09S7xgBJDZMjBvkZxZw=; b=Qp9t8wQsxgKhL9aUaOJeSBGeLiyBeW6cU9nKajR8VrQ/p0i6gmGNLDufRbr7YCUvve 6OAVztFE1cuRBF6qabTp1jg8infZKrUhzKF12/k7x2idIkSCTgP9x88Z0kBSjqM5jbxu mBgabLN/8ctsQ5c2StPxSyEqbMMBh1jYr78svZJ7xwz3+rFCnEcW/Ja5qcOXxxzKqeu8 dZNQ/h8T9P7R/hD8lTru3YJo3ErXN/IS5F/BS6qDkna/VVWsB9DmPPFuYvZGEYifd5AJ UaMCcYraN4HCIlJ0p/pg72egVo/Wbw0TwzLQmI26Uxm0oIm6/TAF7Osa1WefOtQxBAf2 PgCg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; t=1680224503; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=AZsj2vydlw7mk9QHWSB9x4ES09S7xgBJDZMjBvkZxZw=; b=vcdCr5Pi7jO7Qka+fNX0Gfe44+c1axyiins/4iJumHmBhaWKATFuVLauI5RbxQyKee e1Ch+Sh6Hz3Iti34oXOaVctxIXtmCPIj1SN7+xJjrKEAz2U9vq4oMiNwVseJKWz4GZrV 8DcTML9plCLCBNsZcmcGGcOCU+SRGcZ7eNq5sw6tEz+p7kqdE7lHxaodf4FJDKTwoflX Fwt9eUZ6vk2XwaHVz3yFccFcV7vnxEMq/psqlkkJWTGcpe4zcaPs+JQaPWPnRqa3T1Nv WzOr2xAyXYWcnN7VzsW3f2CLEpcAT/Is723vrOX1Nh++Kmq+f/2hocQSK4LDVJK854xm ZrIw== X-Gm-Message-State: AAQBX9cdRF9/Ms+eD+0HJpE2QEsoWHj/gyqiScqpgoJXjJg0BW5uEZoX gKyVk24w1r3Bk7jUSVa36YE674XTqLDem4Zg0GHEhrocvLNs/d099X5llHhvqOZpOToK8N1/BBf +WCd1VaTOGWSbvU/9knTEBnjtVn34drEsvZQdZmnoSeIogaGT6H3QnlYC3JdaMB8= X-Google-Smtp-Source: AKy350ZV4ik+pdcMtdbO43eYqD8ZjMKcpieFJP4ROqeSo3ZXmF4qLiyoJ6M4rSZ93pS6qnUO+5KLqwc9KQxh X-Received: from komlodi.c.googlers.com ([fda3:e722:ac3:cc00:7f:e700:c0a8:35ee]) (user=komlodi job=sendgmr) by 2002:a17:902:dac9:b0:1a2:1c7:1c1f with SMTP id q9-20020a170902dac900b001a201c71c1fmr9786616plx.7.1680224503353; Thu, 30 Mar 2023 18:01:43 -0700 (PDT) Date: Fri, 31 Mar 2023 01:01:21 +0000 In-Reply-To: <20230331010131.1412571-1-komlodi@google.com> Mime-Version: 1.0 References: <20230331010131.1412571-1-komlodi@google.com> X-Mailer: git-send-email 2.40.0.348.gf938b09366-goog Message-ID: <20230331010131.1412571-7-komlodi@google.com> Subject: [PATCH 06/16] hw/i3c/aspeed_i3c: Treat more registers as read-as-zero From: Joe Komlodi To: qemu-devel@nongnu.org Cc: venture@google.com, komlodi@google.com, peter.maydell@linaro.org Received-SPF: pass client-ip=2607:f8b0:4864:20::104a; envelope-from=39zAmZAcKChoAECBE386EE6B4.2ECG4CK-34L4BDED6DK.EH6@flex--komlodi.bounces.google.com; helo=mail-pj1-x104a.google.com X-Spam_score_int: -95 X-Spam_score: -9.6 X-Spam_bar: --------- X-Spam_report: (-9.6 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_MED=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, USER_IN_DEF_DKIM_WL=-7.5 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+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org RESET_CTRL and INTR_FORCE are write-only. Signed-off-by: Joe Komlodi Reviewed-by: Patrick Venture --- hw/i3c/aspeed_i3c.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hw/i3c/aspeed_i3c.c b/hw/i3c/aspeed_i3c.c index 6f514bef5e..79715f462d 100644 --- a/hw/i3c/aspeed_i3c.c +++ b/hw/i3c/aspeed_i3c.c @@ -420,7 +420,10 @@ static uint64_t aspeed_i3c_device_read(void *opaque, hwaddr offset, uint64_t value; switch (addr) { + /* RAZ */ case R_COMMAND_QUEUE_PORT: + case R_RESET_CTRL: + case R_INTR_FORCE: value = 0; break; default: From patchwork Fri Mar 31 01:01:22 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Joe Komlodi X-Patchwork-Id: 1763553 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=nongnu.org (client-ip=209.51.188.17; helo=lists.gnu.org; envelope-from=qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org; receiver=) Authentication-Results: legolas.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=google.com header.i=@google.com header.a=rsa-sha256 header.s=20210112 header.b=o8L7wtD1; dkim-atps=neutral Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-ECDSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4PnhqQ2Wyqz1yYr for ; Fri, 31 Mar 2023 12:03:14 +1100 (AEDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1pi39J-0003Dd-NM; Thu, 30 Mar 2023 21:01:49 -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 <3-DAmZAcKChsBFDCF497FF7C5.3FDH5DL-45M5CEFE7EL.FI7@flex--komlodi.bounces.google.com>) id 1pi39I-0003D6-Bc for qemu-devel@nongnu.org; Thu, 30 Mar 2023 21:01:48 -0400 Received: from mail-pf1-x449.google.com ([2607:f8b0:4864:20::449]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from <3-DAmZAcKChsBFDCF497FF7C5.3FDH5DL-45M5CEFE7EL.FI7@flex--komlodi.bounces.google.com>) id 1pi39G-0006b5-Rs for qemu-devel@nongnu.org; Thu, 30 Mar 2023 21:01:48 -0400 Received: by mail-pf1-x449.google.com with SMTP id i26-20020aa796fa000000b006261da7aeceso9538682pfq.5 for ; Thu, 30 Mar 2023 18:01:45 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20210112; t=1680224505; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=NSxgqMkN76sf5i6jVWeEWEGMcfgMjXp+O1ZiyMeH4SQ=; b=o8L7wtD1IgMev5WTcAGcXqWUNOeQsdepMRIgclkB+2GfZ/1PEx4bdil/2vf3PWhjFo L+9sLnGOf9H2eAkTkyV48Mme1jQfY+udWbgbYcnFNwx0UTtYuZK6Ymu+1DxJokH76Fii ckj2hH01r8CZCV9iloqQoNPXIk1CebwXvjP44NmuluKrP9jIBa17g9gbLTqWGc3trOmN czsZbjSBJonD4KQJgDg62fS9DIQqSCyg9rYiF/HDLaBzBaDUNBhJq7mBxMq3rTAnlUVe gxvR2+BJidnl92wGYjNk7dFJhnWp08huHjZS/A2zsDFieLaMgrOe5wi4FcZe1q3C32i/ 681Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; t=1680224505; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=NSxgqMkN76sf5i6jVWeEWEGMcfgMjXp+O1ZiyMeH4SQ=; b=kdeeq0iMTmXS8qQg3u197bOB5iLLHkyyFsSoQo6AQtbnNcqsYrT2nOfoYLdY4CkctF tq/0SRfFOSMmMD8nyPxCd6jBhsKHXGJyfU+sNZcV57sA59hxWqe58ypAisBSlo83mLYE QlstFySzRShEl2jRkTM+NZciWpBtNYD6Ix8TUT3CeLvycifRXLe0Od76ZqYTehEMajjH XMzuve87M8sMwfNL0KAtv4RcrULBRkl/K8tra0flxRXmKqyv1/i1/3HyXxS+H/9lHuFk xGGN1KPrBoBpDBdPnk60whx47cEkDjmc8bQcnuo6vnaUF383DQb1Amv7RRDHSNg+DzF+ +iQQ== X-Gm-Message-State: AAQBX9dGssUXmnd0UyM9mOCue4dvU6GeYxuZzLXZz93Z+xdbSDuWByom 57GZKuPH+WausP686EpnzMfcSKyn+LbQs0Hw5UKAddT71sirYoYLBBJA6ucO8YG419Y7JEaK1SS xKga0XBQr9Huncu9Wy9TB+rC3kT4z56fGmTc/iZ0DmJ8A7F47K3r4JgNuWixf8Xg= X-Google-Smtp-Source: AKy350aIts+j/jJCbFyHyfH6vY8hLBQVpfrntaX0K7I1uEIrshSNmqkP1G286kVjpClJZTpHtPFsurrwBJSj X-Received: from komlodi.c.googlers.com ([fda3:e722:ac3:cc00:7f:e700:c0a8:35ee]) (user=komlodi job=sendgmr) by 2002:a17:90a:2dce:b0:23d:33e5:33ec with SMTP id q14-20020a17090a2dce00b0023d33e533ecmr7951388pjm.1.1680224504784; Thu, 30 Mar 2023 18:01:44 -0700 (PDT) Date: Fri, 31 Mar 2023 01:01:22 +0000 In-Reply-To: <20230331010131.1412571-1-komlodi@google.com> Mime-Version: 1.0 References: <20230331010131.1412571-1-komlodi@google.com> X-Mailer: git-send-email 2.40.0.348.gf938b09366-goog Message-ID: <20230331010131.1412571-8-komlodi@google.com> Subject: [PATCH 07/16] hw/i3c/aspeed_i3c: Use 32 bits on MMIO writes From: Joe Komlodi To: qemu-devel@nongnu.org Cc: venture@google.com, komlodi@google.com, peter.maydell@linaro.org Received-SPF: pass client-ip=2607:f8b0:4864:20::449; envelope-from=3-DAmZAcKChsBFDCF497FF7C5.3FDH5DL-45M5CEFE7EL.FI7@flex--komlodi.bounces.google.com; helo=mail-pf1-x449.google.com X-Spam_score_int: -95 X-Spam_score: -9.6 X-Spam_bar: --------- X-Spam_report: (-9.6 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_MED=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, USER_IN_DEF_DKIM_WL=-7.5 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+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org The registers are only 32 bits wide, so we should cast the 64-bit value passed in to only be 32 bits wide. Signed-off-by: Joe Komlodi Reviewed-by: Patrick Venture Reviewed-by: Titus Rwantare --- hw/i3c/aspeed_i3c.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/hw/i3c/aspeed_i3c.c b/hw/i3c/aspeed_i3c.c index 79715f462d..2ed09234ff 100644 --- a/hw/i3c/aspeed_i3c.c +++ b/hw/i3c/aspeed_i3c.c @@ -441,10 +441,11 @@ static void aspeed_i3c_device_write(void *opaque, hwaddr offset, { AspeedI3CDevice *s = ASPEED_I3C_DEVICE(opaque); uint32_t addr = offset >> 2; + uint32_t val32 = (uint32_t)value; trace_aspeed_i3c_device_write(s->id, offset, value); - value &= ~ast2600_i3c_device_ro[addr]; + val32 &= ~ast2600_i3c_device_ro[addr]; switch (addr) { case R_HW_CAPABILITY: case R_RESPONSE_QUEUE_PORT: @@ -470,7 +471,7 @@ static void aspeed_i3c_device_write(void *opaque, hwaddr offset, case R_RESET_CTRL: break; default: - s->regs[addr] = value; + s->regs[addr] = val32; break; } } From patchwork Fri Mar 31 01:01:23 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Joe Komlodi X-Patchwork-Id: 1763556 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=nongnu.org (client-ip=209.51.188.17; helo=lists.gnu.org; envelope-from=qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org; receiver=) Authentication-Results: legolas.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=google.com header.i=@google.com header.a=rsa-sha256 header.s=20210112 header.b=BQBJOsKA; dkim-atps=neutral Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-ECDSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4Pnhsg3TDVz1yY8 for ; Fri, 31 Mar 2023 12:05:11 +1100 (AEDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1pi39M-0003ER-9j; Thu, 30 Mar 2023 21:01:52 -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 <3-jAmZAcKCh0DHFEH6B9HH9E7.5HFJ7FN-67O7EGHG9GN.HK9@flex--komlodi.bounces.google.com>) id 1pi39K-0003Dx-2V for qemu-devel@nongnu.org; Thu, 30 Mar 2023 21:01:50 -0400 Received: from mail-pf1-x449.google.com ([2607:f8b0:4864:20::449]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from <3-jAmZAcKCh0DHFEH6B9HH9E7.5HFJ7FN-67O7EGHG9GN.HK9@flex--komlodi.bounces.google.com>) id 1pi39I-0006bM-Ec for qemu-devel@nongnu.org; Thu, 30 Mar 2023 21:01:49 -0400 Received: by mail-pf1-x449.google.com with SMTP id t67-20020a628146000000b0062d6d838243so7217422pfd.21 for ; Thu, 30 Mar 2023 18:01:47 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20210112; t=1680224506; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=29sNXL7y+2uWwg7caIe+EWdFRWQP6+Wn+OUoGeGi/Jo=; b=BQBJOsKAqmaZMy/H8CVuvp8K345fALxhpgKndYJnHu+gN6OyiZ/hooYEL25bFos2in BLTyqBHadQPf3C8hkjHYXtlIlt7NtCBxRUlTjCCAyjF/r52ORv4rChfXFtC6+9AC7x+e H6f6FnV0NBGpBNNXemQ7fGttzpvdyiuw3wOU2IfrcLfArZQ6ZEtbFFe4dWpz1XC6iE+b KpjwYZkbKV4lQOSR7+jNXfm99VPCAgzVehzGhtqeImAki+7y4UX6SMZBMKTGynRGGo41 34CFfM2dW3d9sV+80cglf3SHDHmShREA2JhMTfKkI8A0PZ4A+UY0rfJbS7eMmSdCIeT+ XRIg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; t=1680224506; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=29sNXL7y+2uWwg7caIe+EWdFRWQP6+Wn+OUoGeGi/Jo=; b=B3QiZ/53T6AgGzKOhr2l4zeG93MXfpn9X8eqtT8h98YGWNeKYkgCQXcxtLGzxQtiUQ 8EqoaAdbRFNxg1ZdF+4n4/EjOMn5ckK6Wky4h0wSE32tff/0dJrU4yk/kz8IchfyO275 EHQ54SohjVOEBh/nq6ok/4ONI2YqvmWK0PX0DvJtKCS9CKDQkimkZwbss+r3r2Jsr1AK HNKg7AE4Fg1BN9Rom5UpZqBNaiZ8hprSo594DyFqMpHcsebFQrd10+3v73sBRqG5+e1b l0n+OEr/MKzkAOljAvDN64Yb75advjf0+PzoZEQjVjWStgOBNhE1yw9behOrHopCku7D iiCw== X-Gm-Message-State: AAQBX9cbkUe8OIIqskGe/5en8FSdXXQPxERfbXsCZqtF0FtI+YUECVR4 +YpOUfHre6TXCyPmVWigbmLTxGMfytzGFqxcs9GSGfo4XCRXh7tAfNPonjJd6opNYoNDyFnGV58 9pglV+5bvpYEgxEG5AHBFK/BmmBJ3IEwINJLChsEfO6Hc9tVJqZbdKkM7aCz5xd8= X-Google-Smtp-Source: AKy350bC7AGRTuZZVaUgsxb2zrhDmW18HzVKUMBZlmJP/GqsNuzGlk9VGxwhlCBwiquvjVrj4OQAzQGSjgts X-Received: from komlodi.c.googlers.com ([fda3:e722:ac3:cc00:7f:e700:c0a8:35ee]) (user=komlodi job=sendgmr) by 2002:a63:e104:0:b0:507:3e33:4390 with SMTP id z4-20020a63e104000000b005073e334390mr2391965pgh.6.1680224506532; Thu, 30 Mar 2023 18:01:46 -0700 (PDT) Date: Fri, 31 Mar 2023 01:01:23 +0000 In-Reply-To: <20230331010131.1412571-1-komlodi@google.com> Mime-Version: 1.0 References: <20230331010131.1412571-1-komlodi@google.com> X-Mailer: git-send-email 2.40.0.348.gf938b09366-goog Message-ID: <20230331010131.1412571-9-komlodi@google.com> Subject: [PATCH 08/16] hw/i3c/aspeed_i3c: Add IRQ MMIO behavior From: Joe Komlodi To: qemu-devel@nongnu.org Cc: venture@google.com, komlodi@google.com, peter.maydell@linaro.org Received-SPF: pass client-ip=2607:f8b0:4864:20::449; envelope-from=3-jAmZAcKCh0DHFEH6B9HH9E7.5HFJ7FN-67O7EGHG9GN.HK9@flex--komlodi.bounces.google.com; helo=mail-pf1-x449.google.com X-Spam_score_int: -95 X-Spam_score: -9.6 X-Spam_bar: --------- X-Spam_report: (-9.6 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_MED=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, USER_IN_DEF_DKIM_WL=-7.5 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+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Signed-off-by: Joe Komlodi Reviewed-by: Patrick Venture Reviewed-by: Hao Wu --- hw/i3c/aspeed_i3c.c | 57 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/hw/i3c/aspeed_i3c.c b/hw/i3c/aspeed_i3c.c index 2ed09234ff..b9aa1367d8 100644 --- a/hw/i3c/aspeed_i3c.c +++ b/hw/i3c/aspeed_i3c.c @@ -17,6 +17,8 @@ #include "qapi/error.h" #include "migration/vmstate.h" #include "trace.h" +#include "hw/i3c/i3c.h" +#include "hw/irq.h" /* I3C Controller Registers */ REG32(I3C1_REG0, 0x10) @@ -412,6 +414,46 @@ static const uint32_t ast2600_i3c_device_ro[ASPEED_I3C_DEVICE_NR_REGS] = { [R_SLAVE_CONFIG] = 0xffffffff, }; +static void aspeed_i3c_device_update_irq(AspeedI3CDevice *s) +{ + bool level = !!(s->regs[R_INTR_SIGNAL_EN] & s->regs[R_INTR_STATUS]); + qemu_set_irq(s->irq, level); +} + +static uint32_t aspeed_i3c_device_intr_status_r(AspeedI3CDevice *s) +{ + /* Only return the status whose corresponding EN bits are set. */ + return s->regs[R_INTR_STATUS] & s->regs[R_INTR_STATUS_EN]; +} + +static void aspeed_i3c_device_intr_status_w(AspeedI3CDevice *s, uint32_t val) +{ + /* INTR_STATUS[13:5] is w1c, other bits are RO. */ + val &= 0x3fe0; + s->regs[R_INTR_STATUS] &= ~val; + + aspeed_i3c_device_update_irq(s); +} + +static void aspeed_i3c_device_intr_status_en_w(AspeedI3CDevice *s, uint32_t val) +{ + s->regs[R_INTR_STATUS_EN] = val; + aspeed_i3c_device_update_irq(s); +} + +static void aspeed_i3c_device_intr_signal_en_w(AspeedI3CDevice *s, uint32_t val) +{ + s->regs[R_INTR_SIGNAL_EN] = val; + aspeed_i3c_device_update_irq(s); +} + +static void aspeed_i3c_device_intr_force_w(AspeedI3CDevice *s, uint32_t val) +{ + /* INTR_FORCE is WO, just set the corresponding INTR_STATUS bits. */ + s->regs[R_INTR_STATUS] = val; + aspeed_i3c_device_update_irq(s); +} + static uint64_t aspeed_i3c_device_read(void *opaque, hwaddr offset, unsigned size) { @@ -426,6 +468,9 @@ static uint64_t aspeed_i3c_device_read(void *opaque, hwaddr offset, case R_INTR_FORCE: value = 0; break; + case R_INTR_STATUS: + value = aspeed_i3c_device_intr_status_r(s); + break; default: value = s->regs[addr]; break; @@ -470,6 +515,18 @@ static void aspeed_i3c_device_write(void *opaque, hwaddr offset, break; case R_RESET_CTRL: break; + case R_INTR_STATUS: + aspeed_i3c_device_intr_status_w(s, val32); + break; + case R_INTR_STATUS_EN: + aspeed_i3c_device_intr_status_en_w(s, val32); + break; + case R_INTR_SIGNAL_EN: + aspeed_i3c_device_intr_signal_en_w(s, val32); + break; + case R_INTR_FORCE: + aspeed_i3c_device_intr_force_w(s, val32); + break; default: s->regs[addr] = val32; break; From patchwork Fri Mar 31 01:01:24 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Joe Komlodi X-Patchwork-Id: 1763563 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=nongnu.org (client-ip=209.51.188.17; helo=lists.gnu.org; envelope-from=qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org; receiver=) Authentication-Results: legolas.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=google.com header.i=@google.com header.a=rsa-sha256 header.s=20210112 header.b=TUNUIXCo; dkim-atps=neutral Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-ECDSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4Pnht80bqJz1yY8 for ; Fri, 31 Mar 2023 12:05:36 +1100 (AEDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1pi39O-0003Eq-SX; Thu, 30 Mar 2023 21:01:54 -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 <3_DAmZAcKCh8FJHGJ8DBJJBG9.7JHL9HP-89Q9GIJIBIP.JMB@flex--komlodi.bounces.google.com>) id 1pi39N-0003EV-Pu for qemu-devel@nongnu.org; Thu, 30 Mar 2023 21:01:53 -0400 Received: from mail-yw1-x1149.google.com ([2607:f8b0:4864:20::1149]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from <3_DAmZAcKCh8FJHGJ8DBJJBG9.7JHL9HP-89Q9GIJIBIP.JMB@flex--komlodi.bounces.google.com>) id 1pi39K-0006bY-1V for qemu-devel@nongnu.org; Thu, 30 Mar 2023 21:01:53 -0400 Received: by mail-yw1-x1149.google.com with SMTP id 00721157ae682-54629ed836aso66152017b3.10 for ; Thu, 30 Mar 2023 18:01:48 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20210112; t=1680224508; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=o0XGgn6W0h+/sbpah/0CqXSzbAJ5CfaCizDvxJmgxSM=; b=TUNUIXCoKzWY9nyHjkKMTuMN13zTdAK2uy0Ov3O+UKvlvFrz8rhOHJmE8YqVJXHJS8 vKHijcijH4SEgT63ZdvPt6UFw8McSkHVd4ohIem+5yXFPd8GiFPcaOc+hrO3fpMi/Iam hsLosfxsOWeeeNb6fAuxjo8IpGOavxmuW0+JJIOhCBl0LMErV7PicWXv53aqOQPeuOz1 Rjy1GhJKyGmfAj5SfZ3DWyoGSaVtL6YNpaN4VxaE7vwjMVthqxzlgatryYlkCHxK0Eua acN7INavEuzDdcvrtPylB4575P9WNPo5y5RcNRJhTEutGmetXbKOafy7RENLAYRk6yUP jf6A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; t=1680224508; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=o0XGgn6W0h+/sbpah/0CqXSzbAJ5CfaCizDvxJmgxSM=; b=vF9JQ0LGJEyd/NsJHAyvpS6NvnCs7YT4hIDqO6wiURnAoSwTXomTWIMlptH91a8/vr in8SfzGtUmKbmra9qmaknWqDAYcxNkM/ASRjovoTyVSlk5uaANZZXMh+AzAG98bSC+9p y9ROnbsA8/eIigKHoAjn3HefJCiF4OK53J7GJwvK/y/idaOAhUSjmOMouwjKBctDXe1W BESLaxD+bsSrbJCy6DTLWwfYg0oucKggk0v7UusJFgLm8ACcWFsF2T08Kfm+GlTcM6bY qYMMGc4GMQY29qokZtESNQSiH4CbYSPI0Oxml4ev3D6T5v49IqKIhkOJLIM/VbgNxlCG E6Aw== X-Gm-Message-State: AAQBX9fdeMI8vIUFXl/LmA5EI9YumuXWrPevjbE8iT0oUvPk3FriBXw0 9tAdHtbLObjH3NfEnH8ZiUYAGQcDXuNb1ARY1s7vlSyN9W5vwFiginEbz8aJfOg4byWKcVOHYuK /hFLg6vrHMPxGshvsd6McStREGmpWoHhTOs39LI+iyZIJXZjHBGg3vk+Y/XwoewA= X-Google-Smtp-Source: AKy350ZGRGj93nvUQnFwN7+vwKVDPuD0Oe9u5t/yyxVGY4VjmY1nbKKxTFnJjpIVtkoCzDBeCbLn6dzx+dQL X-Received: from komlodi.c.googlers.com ([fda3:e722:ac3:cc00:7f:e700:c0a8:35ee]) (user=komlodi job=sendgmr) by 2002:a05:6902:709:b0:a67:c976:c910 with SMTP id k9-20020a056902070900b00a67c976c910mr12943422ybt.7.1680224508471; Thu, 30 Mar 2023 18:01:48 -0700 (PDT) Date: Fri, 31 Mar 2023 01:01:24 +0000 In-Reply-To: <20230331010131.1412571-1-komlodi@google.com> Mime-Version: 1.0 References: <20230331010131.1412571-1-komlodi@google.com> X-Mailer: git-send-email 2.40.0.348.gf938b09366-goog Message-ID: <20230331010131.1412571-10-komlodi@google.com> Subject: [PATCH 09/16] hw/i3c/aspeed_i3c: Add data TX and RX From: Joe Komlodi To: qemu-devel@nongnu.org Cc: venture@google.com, komlodi@google.com, peter.maydell@linaro.org Received-SPF: pass client-ip=2607:f8b0:4864:20::1149; envelope-from=3_DAmZAcKCh8FJHGJ8DBJJBG9.7JHL9HP-89Q9GIJIBIP.JMB@flex--komlodi.bounces.google.com; helo=mail-yw1-x1149.google.com X-Spam_score_int: -95 X-Spam_score: -9.6 X-Spam_bar: --------- X-Spam_report: (-9.6 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_MED=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, USER_IN_DEF_DKIM_WL=-7.5 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+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org This adds data and CCC transmission, reception, and the associated queues required for data transmission and reception to happen. The I3C controller transmits data by the user writing into a command queue. When the queue has a command and an argument in it, the controller starts executing the command. The controller can execute 1 of 3 ways: 1. A larger data transfer that involves using the TX and RX queues. This is the most common way the controller does transactions. 2. A small data transfer that involves sending a couple bytes passed into the command queue argument. 3. An address assignment command. This is how the controller does ENTDAA. When ENTDAA succeeds in assigning an address to a target, it updates the controller's char table with the target's PID, BCR, and DCR. The controller determines what addresses to send by looking at the index in the device address table specified by the argument in the command queue. ENTDAA also uses these addresses to assign to targets on the bus. When the controller is done executing a command, it puts a response in the response queue indicating how command execution went. In order for the user to send and receive data to/from the controller, the user reads/writes to a bidirectional TX/RX port. Signed-off-by: Joe Komlodi Reviewed-by: Stephen Longfield Reviewed-by: Patrick Venture --- hw/i3c/aspeed_i3c.c | 848 ++++++++++++++++++++++++++++++++++++ hw/i3c/trace-events | 10 + include/hw/i3c/aspeed_i3c.h | 132 ++++++ 3 files changed, 990 insertions(+) diff --git a/hw/i3c/aspeed_i3c.c b/hw/i3c/aspeed_i3c.c index b9aa1367d8..ffe0b21ca0 100644 --- a/hw/i3c/aspeed_i3c.c +++ b/hw/i3c/aspeed_i3c.c @@ -414,12 +414,183 @@ static const uint32_t ast2600_i3c_device_ro[ASPEED_I3C_DEVICE_NR_REGS] = { [R_SLAVE_CONFIG] = 0xffffffff, }; +static inline bool aspeed_i3c_device_has_entdaa(AspeedI3CDevice *s) +{ + return ARRAY_FIELD_EX32(s->regs, HW_CAPABILITY, ENTDAA); +} + +static inline bool aspeed_i3c_device_has_hdr_ts(AspeedI3CDevice *s) +{ + return ARRAY_FIELD_EX32(s->regs, HW_CAPABILITY, HDR_TS); +} + +static inline bool aspeed_i3c_device_has_hdr_ddr(AspeedI3CDevice *s) +{ + return ARRAY_FIELD_EX32(s->regs, HW_CAPABILITY, HDR_DDR); +} + +static inline bool aspeed_i3c_device_can_transmit(AspeedI3CDevice *s) +{ + /* + * We can only transmit if we're enabled and the resume bit is cleared. + * The resume bit is set on a transaction error, and software must clear it. + */ + return ARRAY_FIELD_EX32(s->regs, DEVICE_CTRL, I3C_EN) && + !ARRAY_FIELD_EX32(s->regs, DEVICE_CTRL, I3C_RESUME); +} + +static inline uint8_t aspeed_i3c_device_fifo_threshold_from_reg(uint8_t regval) +{ + return regval = regval ? (2 << regval) : 1; +} + static void aspeed_i3c_device_update_irq(AspeedI3CDevice *s) { bool level = !!(s->regs[R_INTR_SIGNAL_EN] & s->regs[R_INTR_STATUS]); qemu_set_irq(s->irq, level); } +static void aspeed_i3c_device_end_transfer(AspeedI3CDevice *s, bool is_i2c) +{ + if (is_i2c) { + legacy_i2c_end_transfer(s->bus); + } else { + i3c_end_transfer(s->bus); + } +} + +static int aspeed_i3c_device_send_start(AspeedI3CDevice *s, uint8_t addr, + bool is_recv, bool is_i2c) +{ + int ret; + + if (is_i2c) { + ret = legacy_i2c_start_transfer(s->bus, addr, is_recv); + } else { + ret = i3c_start_transfer(s->bus, addr, is_recv); + } + if (ret) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: NACKed on TX with addr 0x%.2x\n", + object_get_canonical_path(OBJECT(s)), addr); + ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_ST_STATUS, + ASPEED_I3C_TRANSFER_STATE_HALT); + ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_STATUS, + ASPEED_I3C_TRANSFER_STATUS_HALT); + ARRAY_FIELD_DP32(s->regs, INTR_STATUS, TRANSFER_ERR, 1); + ARRAY_FIELD_DP32(s->regs, DEVICE_CTRL, I3C_RESUME, 1); + } + + return ret; +} + +static int aspeed_i3c_device_send(AspeedI3CDevice *s, const uint8_t *data, + uint32_t num_to_send, uint32_t *num_sent, + bool is_i2c) +{ + int ret; + uint32_t i; + + *num_sent = 0; + if (is_i2c) { + /* Legacy I2C must be byte-by-byte. */ + for (i = 0; i < num_to_send; i++) { + ret = legacy_i2c_send(s->bus, data[i]); + if (ret) { + break; + } + (*num_sent)++; + } + } else { + ret = i3c_send(s->bus, data, num_to_send, num_sent); + } + if (ret) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: NACKed sending byte 0x%.2x\n", + object_get_canonical_path(OBJECT(s)), data[*num_sent]); + ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_ST_STATUS, + ASPEED_I3C_TRANSFER_STATE_HALT); + ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_STATUS, + ASPEED_I3C_TRANSFER_STATUS_HALT); + ARRAY_FIELD_DP32(s->regs, INTR_STATUS, TRANSFER_ERR, 1); + ARRAY_FIELD_DP32(s->regs, DEVICE_CTRL, I3C_RESUME, 1); + } + + trace_aspeed_i3c_device_send(s->id, *num_sent); + + return ret; +} + +static int aspeed_i3c_device_send_byte(AspeedI3CDevice *s, uint8_t byte, + bool is_i2c) +{ + /* + * Ignored, the caller will know if we sent 0 or 1 bytes depending on if + * we were ACKed/NACKed. + */ + uint32_t num_sent; + return aspeed_i3c_device_send(s, &byte, 1, &num_sent, is_i2c); +} + +static int aspeed_i3c_device_recv_data(AspeedI3CDevice *s, bool is_i2c, + uint8_t *data, uint16_t num_to_read, + uint32_t *num_read) +{ + int ret; + + if (is_i2c) { + for (uint16_t i = 0; i < num_to_read; i++) { + data[i] = legacy_i2c_recv(s->bus); + } + /* I2C devices can neither NACK a read, nor end transfers early. */ + *num_read = num_to_read; + trace_aspeed_i3c_device_recv_data(s->id, *num_read); + return 0; + } + /* I3C devices can NACK if the controller sends an unsupported CCC. */ + ret = i3c_recv(s->bus, data, num_to_read, num_read); + if (ret) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: NACKed receiving byte\n", + object_get_canonical_path(OBJECT(s))); + ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_ST_STATUS, + ASPEED_I3C_TRANSFER_STATE_HALT); + ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_STATUS, + ASPEED_I3C_TRANSFER_STATUS_HALT); + ARRAY_FIELD_DP32(s->regs, INTR_STATUS, TRANSFER_ERR, 1); + ARRAY_FIELD_DP32(s->regs, DEVICE_CTRL, I3C_RESUME, 1); + } + + trace_aspeed_i3c_device_recv_data(s->id, *num_read); + + return ret; +} + +static inline bool aspeed_i3c_device_target_is_i2c(AspeedI3CDevice *s, + uint16_t offset) +{ + uint16_t dev_index = R_DEVICE_ADDR_TABLE_LOC1 + offset; + return FIELD_EX32(s->regs[dev_index], DEVICE_ADDR_TABLE_LOC1, + LEGACY_I2C_DEVICE); +} + +static uint8_t aspeed_i3c_device_target_addr(AspeedI3CDevice *s, + uint16_t offset) +{ + if (offset > ASPEED_I3C_NR_DEVICES) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Device addr table offset %d out of " + "bounds\n", object_get_canonical_path(OBJECT(s)), offset); + /* If we're out of bounds, return an address of 0. */ + return 0; + } + + uint16_t dev_index = R_DEVICE_ADDR_TABLE_LOC1 + offset; + /* I2C devices use a static address. */ + if (aspeed_i3c_device_target_is_i2c(s, offset)) { + return FIELD_EX32(s->regs[dev_index], DEVICE_ADDR_TABLE_LOC1, + DEV_STATIC_ADDR); + } + return FIELD_EX32(s->regs[dev_index], DEVICE_ADDR_TABLE_LOC1, + DEV_DYNAMIC_ADDR); +} + static uint32_t aspeed_i3c_device_intr_status_r(AspeedI3CDevice *s) { /* Only return the status whose corresponding EN bits are set. */ @@ -454,6 +625,54 @@ static void aspeed_i3c_device_intr_force_w(AspeedI3CDevice *s, uint32_t val) aspeed_i3c_device_update_irq(s); } +static uint32_t aspeed_i3c_device_pop_rx(AspeedI3CDevice *s) +{ + if (fifo32_is_empty(&s->rx_queue)) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Tried to read RX FIFO when empty\n", + object_get_canonical_path(OBJECT(s))); + return 0; + } + + uint32_t val = fifo32_pop(&s->rx_queue); + ARRAY_FIELD_DP32(s->regs, DATA_BUFFER_STATUS_LEVEL, RX_BUF_BLR, + fifo32_num_used(&s->rx_queue)); + + /* Threshold is 2^RX_BUF_THLD. */ + uint8_t threshold = ARRAY_FIELD_EX32(s->regs, DATA_BUFFER_THLD_CTRL, + RX_BUF_THLD); + threshold = aspeed_i3c_device_fifo_threshold_from_reg(threshold); + if (fifo32_num_used(&s->rx_queue) < threshold) { + ARRAY_FIELD_DP32(s->regs, INTR_STATUS, RX_THLD, 0); + aspeed_i3c_device_update_irq(s); + } + + trace_aspeed_i3c_device_pop_rx(s->id, val); + return val; +} + +static uint32_t aspeed_i3c_device_resp_queue_port_r(AspeedI3CDevice *s) +{ + if (fifo32_is_empty(&s->resp_queue)) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Tried to read response FIFO when " + "empty\n", object_get_canonical_path(OBJECT(s))); + return 0; + } + + uint32_t val = fifo32_pop(&s->resp_queue); + ARRAY_FIELD_DP32(s->regs, QUEUE_STATUS_LEVEL, RESP_BUF_BLR, + fifo32_num_used(&s->resp_queue)); + + /* Threshold is the register value + 1. */ + uint8_t threshold = ARRAY_FIELD_EX32(s->regs, QUEUE_THLD_CTRL, + RESP_BUF_THLD) + 1; + if (fifo32_num_used(&s->resp_queue) < threshold) { + ARRAY_FIELD_DP32(s->regs, INTR_STATUS, RESP_RDY, 0); + aspeed_i3c_device_update_irq(s); + } + + return val; +} + static uint64_t aspeed_i3c_device_read(void *opaque, hwaddr offset, unsigned size) { @@ -471,6 +690,12 @@ static uint64_t aspeed_i3c_device_read(void *opaque, hwaddr offset, case R_INTR_STATUS: value = aspeed_i3c_device_intr_status_r(s); break; + case R_RX_TX_DATA_PORT: + value = aspeed_i3c_device_pop_rx(s); + break; + case R_RESPONSE_QUEUE_PORT: + value = aspeed_i3c_device_resp_queue_port_r(s); + break; default: value = s->regs[addr]; break; @@ -481,6 +706,618 @@ static uint64_t aspeed_i3c_device_read(void *opaque, hwaddr offset, return value; } +static void aspeed_i3c_device_resp_queue_push(AspeedI3CDevice *s, + uint8_t err, uint8_t tid, + uint8_t ccc_type, + uint16_t data_len) +{ + uint32_t val = 0; + val = FIELD_DP32(val, RESPONSE_QUEUE_PORT, ERR_STATUS, err); + val = FIELD_DP32(val, RESPONSE_QUEUE_PORT, TID, tid); + val = FIELD_DP32(val, RESPONSE_QUEUE_PORT, CCCT, ccc_type); + val = FIELD_DP32(val, RESPONSE_QUEUE_PORT, DL, data_len); + if (!fifo32_is_full(&s->resp_queue)) { + trace_aspeed_i3c_device_resp_queue_push(s->id, val); + fifo32_push(&s->resp_queue, val); + } + + ARRAY_FIELD_DP32(s->regs, QUEUE_STATUS_LEVEL, RESP_BUF_BLR, + fifo32_num_used(&s->resp_queue)); + /* Threshold is the register value + 1. */ + uint8_t threshold = ARRAY_FIELD_EX32(s->regs, QUEUE_THLD_CTRL, + RESP_BUF_THLD) + 1; + if (fifo32_num_used(&s->resp_queue) >= threshold) { + ARRAY_FIELD_DP32(s->regs, INTR_STATUS, RESP_RDY, 1); + aspeed_i3c_device_update_irq(s); + } +} + +static void aspeed_i3c_device_push_tx(AspeedI3CDevice *s, uint32_t val) +{ + if (fifo32_is_full(&s->tx_queue)) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Tried to push to TX FIFO when " + "full\n", object_get_canonical_path(OBJECT(s))); + return; + } + + trace_aspeed_i3c_device_push_tx(s->id, val); + fifo32_push(&s->tx_queue, val); + ARRAY_FIELD_DP32(s->regs, DATA_BUFFER_STATUS_LEVEL, TX_BUF_EMPTY_LOC, + fifo32_num_free(&s->tx_queue)); + + /* Threshold is 2^TX_BUF_THLD. */ + uint8_t empty_threshold = ARRAY_FIELD_EX32(s->regs, DATA_BUFFER_THLD_CTRL, + TX_BUF_THLD); + empty_threshold = + aspeed_i3c_device_fifo_threshold_from_reg(empty_threshold); + if (fifo32_num_free(&s->tx_queue) < empty_threshold) { + ARRAY_FIELD_DP32(s->regs, INTR_STATUS, TX_THLD, 0); + aspeed_i3c_device_update_irq(s); + } +} + +static uint32_t aspeed_i3c_device_pop_tx(AspeedI3CDevice *s) +{ + if (fifo32_is_empty(&s->tx_queue)) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Tried to pop from TX FIFO when " + "empty\n", object_get_canonical_path(OBJECT(s))); + return 0; + } + + uint32_t val = fifo32_pop(&s->tx_queue); + trace_aspeed_i3c_device_pop_tx(s->id, val); + ARRAY_FIELD_DP32(s->regs, DATA_BUFFER_STATUS_LEVEL, TX_BUF_EMPTY_LOC, + fifo32_num_free(&s->tx_queue)); + + /* Threshold is 2^TX_BUF_THLD. */ + uint8_t empty_threshold = ARRAY_FIELD_EX32(s->regs, DATA_BUFFER_THLD_CTRL, + TX_BUF_THLD); + empty_threshold = + aspeed_i3c_device_fifo_threshold_from_reg(empty_threshold); + if (fifo32_num_free(&s->tx_queue) >= empty_threshold) { + ARRAY_FIELD_DP32(s->regs, INTR_STATUS, TX_THLD, 1); + aspeed_i3c_device_update_irq(s); + } + return val; +} + +static void aspeed_i3c_device_push_rx(AspeedI3CDevice *s, uint32_t val) +{ + if (fifo32_is_full(&s->rx_queue)) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Tried to push to RX FIFO when " + "full\n", object_get_canonical_path(OBJECT(s))); + return; + } + trace_aspeed_i3c_device_push_rx(s->id, val); + fifo32_push(&s->rx_queue, val); + + ARRAY_FIELD_DP32(s->regs, DATA_BUFFER_STATUS_LEVEL, RX_BUF_BLR, + fifo32_num_used(&s->rx_queue)); + /* Threshold is 2^RX_BUF_THLD. */ + uint8_t threshold = ARRAY_FIELD_EX32(s->regs, DATA_BUFFER_THLD_CTRL, + RX_BUF_THLD); + threshold = aspeed_i3c_device_fifo_threshold_from_reg(threshold); + if (fifo32_num_used(&s->rx_queue) >= threshold) { + ARRAY_FIELD_DP32(s->regs, INTR_STATUS, RX_THLD, 1); + aspeed_i3c_device_update_irq(s); + } +} + +static void aspeed_i3c_device_short_transfer(AspeedI3CDevice *s, + AspeedI3CTransferCmd cmd, + AspeedI3CShortArg arg) +{ + uint8_t err = ASPEED_I3C_RESP_QUEUE_ERR_NONE; + uint8_t addr = aspeed_i3c_device_target_addr(s, cmd.dev_index); + bool is_i2c = aspeed_i3c_device_target_is_i2c(s, cmd.dev_index); + uint8_t data[4]; /* Max we can send on a short transfer is 4 bytes. */ + uint8_t len = 0; + uint32_t bytes_sent; /* Ignored on short transfers. */ + + /* Can't do reads on a short transfer. */ + if (cmd.rnw) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Cannot do a read on a short " + "transfer\n", object_get_canonical_path(OBJECT(s))); + return; + } + + if (aspeed_i3c_device_send_start(s, addr, /*is_recv=*/false, is_i2c)) { + err = ASPEED_I3C_RESP_QUEUE_ERR_I2C_NACK; + goto transfer_done; + } + + /* Are we sending a command? */ + if (cmd.cp) { + data[len] = cmd.cmd; + len++; + /* + * byte0 is the defining byte for a command, and is only sent if a + * command is present and if the command has a defining byte present. + * (byte_strb & 0x01) is always treated as set by the controller, and is + * ignored. + */ + if (cmd.dbp) { + data[len] += arg.byte0; + len++; + } + } + + /* Send the bytes passed in the argument. */ + if (arg.byte_strb & 0x02) { + data[len] = arg.byte1; + len++; + } + if (arg.byte_strb & 0x04) { + data[len] = arg.byte2; + len++; + } + + if (aspeed_i3c_device_send(s, data, len, &bytes_sent, is_i2c)) { + err = ASPEED_I3C_RESP_QUEUE_ERR_I2C_NACK; + } else { + /* Only go to an idle state on a successful transfer. */ + ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_ST_STATUS, + ASPEED_I3C_TRANSFER_STATE_IDLE); + } + +transfer_done: + if (cmd.toc) { + aspeed_i3c_device_end_transfer(s, is_i2c); + } + if (cmd.roc) { + /* + * ccc_type is always 0 in controller mode, data_len is 0 in short + * transfers. + */ + aspeed_i3c_device_resp_queue_push(s, err, cmd.tid, /*ccc_type=*/0, + /*data_len=*/0); + } +} + +/* Returns number of bytes transmitted. */ +static uint16_t aspeed_i3c_device_tx(AspeedI3CDevice *s, uint16_t num, + bool is_i2c) +{ + uint16_t bytes_sent = 0; + union { + uint8_t b[sizeof(uint32_t)]; + uint32_t val; + } val32; + + while (bytes_sent < num) { + val32.val = aspeed_i3c_device_pop_tx(s); + for (uint8_t i = 0; i < sizeof(val32.val); i++) { + if (aspeed_i3c_device_send_byte(s, val32.b[i], is_i2c)) { + return bytes_sent; + } + bytes_sent++; + + /* We're not sending the full 32-bits, break early. */ + if (bytes_sent >= num) { + break; + } + } + } + + return bytes_sent; +} + +/* Returns number of bytes received. */ +static uint16_t aspeed_i3c_device_rx(AspeedI3CDevice *s, uint16_t num, + bool is_i2c) +{ + /* + * Allocate a temporary buffer to read data from the target. + * Zero it and word-align it as well in case we're reading unaligned data. + */ + g_autofree uint8_t *data = g_new0(uint8_t, num + (num & 0x03)); + uint32_t *data32 = (uint32_t *)data; + /* + * 32-bits since the I3C API wants a 32-bit number, even though the + * controller can only do 16-bit transfers. + */ + uint32_t num_read = 0; + + /* Can NACK if the target receives an unsupported CCC. */ + if (aspeed_i3c_device_recv_data(s, is_i2c, data, num, &num_read)) { + return 0; + } + + for (uint16_t i = 0; i < num_read / 4; i++) { + aspeed_i3c_device_push_rx(s, *data32); + data32++; + } + /* + * If we're pushing data that isn't 32-bit aligned, push what's left. + * It's software's responsibility to know what bits are valid in the partial + * data. + */ + if (num_read & 0x03) { + aspeed_i3c_device_push_rx(s, *data32); + } + + return num_read; +} + +static int aspeed_i3c_device_transfer_ccc(AspeedI3CDevice *s, + AspeedI3CTransferCmd cmd, + AspeedI3CTransferArg arg) +{ + /* CCC start is always a write. CCCs cannot be done on I2C devices. */ + if (aspeed_i3c_device_send_start(s, I3C_BROADCAST, /*is_recv=*/false, + /*is_i2c=*/false)) { + return ASPEED_I3C_RESP_QUEUE_ERR_BROADCAST_NACK; + } + trace_aspeed_i3c_device_transfer_ccc(s->id, cmd.cmd); + if (aspeed_i3c_device_send_byte(s, cmd.cmd, /*is_i2c=*/false)) { + return ASPEED_I3C_RESP_QUEUE_ERR_I2C_NACK; + } + + /* On a direct CCC, we do a restart and then send the target's address. */ + if (CCC_IS_DIRECT(cmd.cmd)) { + bool is_recv = cmd.rnw; + uint8_t addr = aspeed_i3c_device_target_addr(s, cmd.dev_index); + if (aspeed_i3c_device_send_start(s, addr, is_recv, /*is_i2c=*/false)) { + return ASPEED_I3C_RESP_QUEUE_ERR_BROADCAST_NACK; + } + } + + return ASPEED_I3C_RESP_QUEUE_ERR_NONE; +} + +static void aspeed_i3c_device_transfer(AspeedI3CDevice *s, + AspeedI3CTransferCmd cmd, + AspeedI3CTransferArg arg) +{ + bool is_recv = cmd.rnw; + uint8_t err = ASPEED_I3C_RESP_QUEUE_ERR_NONE; + uint8_t addr = aspeed_i3c_device_target_addr(s, cmd.dev_index); + bool is_i2c = aspeed_i3c_device_target_is_i2c(s, cmd.dev_index); + uint16_t bytes_transferred = 0; + + if (cmd.cp) { + /* We're sending a CCC. */ + err = aspeed_i3c_device_transfer_ccc(s, cmd, arg); + if (err != ASPEED_I3C_RESP_QUEUE_ERR_NONE) { + goto transfer_done; + } + } else { + if (ARRAY_FIELD_EX32(s->regs, DEVICE_CTRL, I3C_BROADCAST_ADDR_INC) && + is_i2c == false) { + if (aspeed_i3c_device_send_start(s, I3C_BROADCAST, + /*is_recv=*/false, is_i2c)) { + err = ASPEED_I3C_RESP_QUEUE_ERR_I2C_NACK; + goto transfer_done; + } + } + /* Otherwise we're doing a private transfer. */ + if (aspeed_i3c_device_send_start(s, addr, is_recv, is_i2c)) { + err = ASPEED_I3C_RESP_QUEUE_ERR_I2C_NACK; + goto transfer_done; + } + } + + if (is_recv) { + bytes_transferred = aspeed_i3c_device_rx(s, arg.data_len, is_i2c); + } else { + bytes_transferred = aspeed_i3c_device_tx(s, arg.data_len, is_i2c); + } + + ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_ST_STATUS, + ASPEED_I3C_TRANSFER_STATE_IDLE); + +transfer_done: + if (cmd.toc) { + aspeed_i3c_device_end_transfer(s, is_i2c); + } + if (cmd.roc) { + /* + * data_len is the number of bytes that still need to be TX'd, or the + * number of bytes RX'd. + */ + uint16_t data_len = is_recv ? bytes_transferred : arg.data_len - + bytes_transferred; + /* CCCT is always 0 in controller mode. */ + aspeed_i3c_device_resp_queue_push(s, err, cmd.tid, /*ccc_type=*/0, + data_len); + } + + aspeed_i3c_device_update_irq(s); +} + +static void aspeed_i3c_device_transfer_cmd(AspeedI3CDevice *s, + AspeedI3CTransferCmd cmd, + AspeedI3CCmdQueueData arg) +{ + uint8_t arg_attr = FIELD_EX32(arg.word, COMMAND_QUEUE_PORT, CMD_ATTR); + + ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CMD_TID, cmd.tid); + + /* User is trying to do HDR transfers, see if we can do them. */ + if (cmd.speed == 0x06 && !aspeed_i3c_device_has_hdr_ddr(s)) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: HDR DDR is not supported\n", + object_get_canonical_path(OBJECT(s))); + ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_ST_STATUS, + ASPEED_I3C_TRANSFER_STATE_HALT); + return; + } + if (cmd.speed == 0x05 && !aspeed_i3c_device_has_hdr_ts(s)) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: HDR TS is not supported\n", + object_get_canonical_path(OBJECT(s))); + ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_ST_STATUS, + ASPEED_I3C_TRANSFER_STATE_HALT); + return; + } + + if (arg_attr == ASPEED_I3C_CMD_ATTR_TRANSFER_ARG) { + aspeed_i3c_device_transfer(s, cmd, arg.transfer_arg); + } else if (arg_attr == ASPEED_I3C_CMD_ATTR_SHORT_DATA_ARG) { + aspeed_i3c_device_short_transfer(s, cmd, arg.short_arg); + } else { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Unknown command queue cmd_attr 0x%x" + "\n", object_get_canonical_path(OBJECT(s)), arg_attr); + ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_ST_STATUS, + ASPEED_I3C_TRANSFER_STATE_HALT); + } +} + +static void aspeed_i3c_device_update_char_table(AspeedI3CDevice *s, + uint8_t offset, uint64_t pid, + uint8_t bcr, uint8_t dcr, + uint8_t addr) +{ + if (offset > ASPEED_I3C_NR_DEVICES) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Device char table offset %d out of " + "bounds\n", object_get_canonical_path(OBJECT(s)), offset); + /* If we're out of bounds, do nothing. */ + return; + } + + /* Each char table index is 128 bits apart. */ + uint16_t dev_index = R_DEVICE_CHARACTERISTIC_TABLE_LOC1 + offset * + sizeof(uint32_t); + s->regs[dev_index] = pid & 0xffffffff; + pid >>= 32; + s->regs[dev_index + 1] = FIELD_DP32(s->regs[dev_index + 1], + DEVICE_CHARACTERISTIC_TABLE_LOC2, + MSB_PID, pid); + s->regs[dev_index + 2] = FIELD_DP32(s->regs[dev_index + 2], + DEVICE_CHARACTERISTIC_TABLE_LOC3, DCR, + dcr); + s->regs[dev_index + 2] = FIELD_DP32(s->regs[dev_index + 2], + DEVICE_CHARACTERISTIC_TABLE_LOC3, BCR, + bcr); + s->regs[dev_index + 3] = FIELD_DP32(s->regs[dev_index + 3], + DEVICE_CHARACTERISTIC_TABLE_LOC4, + DEV_DYNAMIC_ADDR, addr); + + /* Increment PRESENT_DEV_CHAR_TABLE_INDEX. */ + uint8_t idx = ARRAY_FIELD_EX32(s->regs, DEV_CHAR_TABLE_POINTER, + PRESENT_DEV_CHAR_TABLE_INDEX); + /* Increment and rollover. */ + idx++; + if (idx >= ARRAY_FIELD_EX32(s->regs, DEV_CHAR_TABLE_POINTER, + DEV_CHAR_TABLE_DEPTH) / 4) { + idx = 0; + } + ARRAY_FIELD_DP32(s->regs, DEV_CHAR_TABLE_POINTER, + PRESENT_DEV_CHAR_TABLE_INDEX, idx); +} + +static void aspeed_i3c_device_addr_assign_cmd(AspeedI3CDevice *s, + AspeedI3CAddrAssignCmd cmd) +{ + uint8_t i = 0; + uint8_t err = ASPEED_I3C_RESP_QUEUE_ERR_NONE; + + if (!aspeed_i3c_device_has_entdaa(s)) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: ENTDAA is not supported\n", + object_get_canonical_path(OBJECT(s))); + return; + } + + /* Tell everyone to ENTDAA. If these error, no one is on the bus. */ + if (aspeed_i3c_device_send_start(s, I3C_BROADCAST, /*is_recv=*/false, + /*is_i2c=*/false)) { + err = ASPEED_I3C_RESP_QUEUE_ERR_BROADCAST_NACK; + goto transfer_done; + } + if (aspeed_i3c_device_send_byte(s, cmd.cmd, /*is_i2c=*/false)) { + err = ASPEED_I3C_RESP_QUEUE_ERR_BROADCAST_NACK; + goto transfer_done; + } + + /* Go through each device in the table and assign it an address. */ + for (i = 0; i < cmd.dev_count; i++) { + uint8_t addr = aspeed_i3c_device_target_addr(s, cmd.dev_index + i); + union { + uint64_t pid:48; + uint8_t bcr; + uint8_t dcr; + uint32_t w[2]; + uint8_t b[8]; + } target_info; + + /* If this fails, there was no one left to ENTDAA. */ + if (aspeed_i3c_device_send_start(s, I3C_BROADCAST, /*is_recv=*/false, + /*is_i2c=*/false)) { + err = ASPEED_I3C_RESP_QUEUE_ERR_BROADCAST_NACK; + break; + } + + /* + * In ENTDAA, we read 8 bytes from the target, which will be the + * target's PID, BCR, and DCR. After that, we send it the dynamic + * address. + * Don't bother checking the number of bytes received, it must send 8 + * bytes during ENTDAA. + */ + uint32_t num_read; + if (aspeed_i3c_device_recv_data(s, /*is_i2c=*/false, target_info.b, + I3C_ENTDAA_SIZE, &num_read)) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Target NACKed ENTDAA CCC\n", + object_get_canonical_path(OBJECT(s))); + err = ASPEED_I3C_RESP_QUEUE_ERR_DAA_NACK; + goto transfer_done; + } + if (aspeed_i3c_device_send_byte(s, addr, /*is_i2c=*/false)) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Target NACKed addr 0x%.2x " + "during ENTDAA\n", + object_get_canonical_path(OBJECT(s)), addr); + err = ASPEED_I3C_RESP_QUEUE_ERR_DAA_NACK; + break; + } + aspeed_i3c_device_update_char_table(s, cmd.dev_index + i, + target_info.pid, target_info.bcr, + target_info.dcr, addr); + + /* Push the PID, BCR, and DCR to the RX queue. */ + aspeed_i3c_device_push_rx(s, target_info.w[0]); + aspeed_i3c_device_push_rx(s, target_info.w[1]); + } + +transfer_done: + /* Do we send a STOP? */ + if (cmd.toc) { + aspeed_i3c_device_end_transfer(s, /*is_i2c=*/false); + } + /* + * For addr assign commands, the length field is the number of devices + * left to assign. CCCT is always 0 in controller mode. + */ + if (cmd.roc) { + aspeed_i3c_device_resp_queue_push(s, err, cmd.tid, /*ccc_type=*/0, + cmd.dev_count - i); + } +} + +static uint32_t aspeed_i3c_device_cmd_queue_pop(AspeedI3CDevice *s) +{ + if (fifo32_is_empty(&s->cmd_queue)) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Tried to dequeue command queue " + "when it was empty\n", + object_get_canonical_path(OBJECT(s))); + return 0; + } + uint32_t val = fifo32_pop(&s->cmd_queue); + + uint8_t empty_threshold = ARRAY_FIELD_EX32(s->regs, QUEUE_THLD_CTRL, + CMD_BUF_EMPTY_THLD); + uint8_t cmd_queue_empty_loc = ARRAY_FIELD_EX32(s->regs, + QUEUE_STATUS_LEVEL, + CMD_QUEUE_EMPTY_LOC); + cmd_queue_empty_loc++; + ARRAY_FIELD_DP32(s->regs, QUEUE_STATUS_LEVEL, CMD_QUEUE_EMPTY_LOC, + cmd_queue_empty_loc); + if (cmd_queue_empty_loc >= empty_threshold) { + ARRAY_FIELD_DP32(s->regs, INTR_STATUS, CMD_QUEUE_RDY, 1); + aspeed_i3c_device_update_irq(s); + } + + return val; +} + +static void aspeed_i3c_device_cmd_queue_execute(AspeedI3CDevice *s) +{ + ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_ST_STATUS, + ASPEED_I3C_TRANSFER_STATE_IDLE); + if (!aspeed_i3c_device_can_transmit(s)) { + return; + } + + /* + * We only start executing when a command is passed into the FIFO. + * We expect there to be a multiple of 2 items in the queue. The first item + * should be an argument to a command, and the command should be the second + * item. + */ + if (fifo32_num_used(&s->cmd_queue) & 1) { + return; + } + + while (!fifo32_is_empty(&s->cmd_queue)) { + AspeedI3CCmdQueueData arg; + arg.word = aspeed_i3c_device_cmd_queue_pop(s); + AspeedI3CCmdQueueData cmd; + cmd.word = aspeed_i3c_device_cmd_queue_pop(s); + trace_aspeed_i3c_device_cmd_queue_execute(s->id, cmd.word, arg.word); + + uint8_t cmd_attr = FIELD_EX32(cmd.word, COMMAND_QUEUE_PORT, CMD_ATTR); + switch (cmd_attr) { + case ASPEED_I3C_CMD_ATTR_TRANSFER_CMD: + aspeed_i3c_device_transfer_cmd(s, cmd.transfer_cmd, arg); + break; + case ASPEED_I3C_CMD_ATTR_ADDR_ASSIGN_CMD: + /* Arg is discarded for addr assign commands. */ + aspeed_i3c_device_addr_assign_cmd(s, cmd.addr_assign_cmd); + break; + case ASPEED_I3C_CMD_ATTR_TRANSFER_ARG: + case ASPEED_I3C_CMD_ATTR_SHORT_DATA_ARG: + qemu_log_mask(LOG_GUEST_ERROR, "%s: Command queue received argument" + " packet when it expected a command packet\n", + object_get_canonical_path(OBJECT(s))); + break; + default: + /* + * The caller's check before queueing an item should prevent this + * from happening. + */ + g_assert_not_reached(); + break; + } + } +} + +static void aspeed_i3c_device_cmd_queue_push(AspeedI3CDevice *s, uint32_t val) +{ + if (fifo32_is_full(&s->cmd_queue)) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Command queue received packet when " + "already full\n", object_get_canonical_path(OBJECT(s))); + return; + } + trace_aspeed_i3c_device_cmd_queue_push(s->id, val); + fifo32_push(&s->cmd_queue, val); + + uint8_t empty_threshold = ARRAY_FIELD_EX32(s->regs, QUEUE_THLD_CTRL, + CMD_BUF_EMPTY_THLD); + uint8_t cmd_queue_empty_loc = ARRAY_FIELD_EX32(s->regs, + QUEUE_STATUS_LEVEL, + CMD_QUEUE_EMPTY_LOC); + if (cmd_queue_empty_loc) { + cmd_queue_empty_loc--; + ARRAY_FIELD_DP32(s->regs, QUEUE_STATUS_LEVEL, CMD_QUEUE_EMPTY_LOC, + cmd_queue_empty_loc); + } + if (cmd_queue_empty_loc < empty_threshold) { + ARRAY_FIELD_DP32(s->regs, INTR_STATUS, CMD_QUEUE_RDY, 0); + aspeed_i3c_device_update_irq(s); + } +} + +static void aspeed_i3c_device_cmd_queue_port_w(AspeedI3CDevice *s, uint32_t val) +{ + uint8_t cmd_attr = FIELD_EX32(val, COMMAND_QUEUE_PORT, CMD_ATTR); + + switch (cmd_attr) { + /* If a command is received we can start executing it. */ + case ASPEED_I3C_CMD_ATTR_TRANSFER_CMD: + case ASPEED_I3C_CMD_ATTR_ADDR_ASSIGN_CMD: + aspeed_i3c_device_cmd_queue_push(s, val); + aspeed_i3c_device_cmd_queue_execute(s); + break; + /* If we get an argument just push it. */ + case ASPEED_I3C_CMD_ATTR_TRANSFER_ARG: + case ASPEED_I3C_CMD_ATTR_SHORT_DATA_ARG: + aspeed_i3c_device_cmd_queue_push(s, val); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: Command queue received packet with " + "unknown cmd attr 0x%x\n", + object_get_canonical_path(OBJECT(s)), cmd_attr); + break; + } +} + static void aspeed_i3c_device_write(void *opaque, hwaddr offset, uint64_t value, unsigned size) { @@ -512,6 +1349,10 @@ static void aspeed_i3c_device_write(void *opaque, hwaddr offset, __func__, offset, value); break; case R_RX_TX_DATA_PORT: + aspeed_i3c_device_push_tx(s, val32); + break; + case R_COMMAND_QUEUE_PORT: + aspeed_i3c_device_cmd_queue_port_w(s, val32); break; case R_RESET_CTRL: break; @@ -566,6 +1407,13 @@ static void aspeed_i3c_device_realize(DeviceState *dev, Error **errp) memory_region_init_io(&s->mr, OBJECT(s), &aspeed_i3c_device_ops, s, name, ASPEED_I3C_DEVICE_NR_REGS << 2); + + fifo32_create(&s->cmd_queue, ASPEED_I3C_CMD_QUEUE_CAPACITY); + fifo32_create(&s->resp_queue, ASPEED_I3C_RESP_QUEUE_CAPACITY); + fifo32_create(&s->tx_queue, ASPEED_I3C_TX_QUEUE_CAPACITY); + fifo32_create(&s->rx_queue, ASPEED_I3C_RX_QUEUE_CAPACITY); + + s->bus = i3c_init_bus(DEVICE(s), name); } static uint64_t aspeed_i3c_read(void *opaque, hwaddr addr, unsigned int size) diff --git a/hw/i3c/trace-events b/hw/i3c/trace-events index cdf7cb07f6..370c08ea45 100644 --- a/hw/i3c/trace-events +++ b/hw/i3c/trace-events @@ -5,6 +5,16 @@ aspeed_i3c_read(uint64_t offset, uint64_t data) "I3C read: offset 0x%" PRIx64 " aspeed_i3c_write(uint64_t offset, uint64_t data) "I3C write: offset 0x%" PRIx64 " data 0x%" PRIx64 aspeed_i3c_device_read(uint32_t deviceid, uint64_t offset, uint64_t data) "I3C Dev[%u] read: offset 0x%" PRIx64 " data 0x%" PRIx64 aspeed_i3c_device_write(uint32_t deviceid, uint64_t offset, uint64_t data) "I3C Dev[%u] write: offset 0x%" PRIx64 " data 0x%" PRIx64 +aspeed_i3c_device_send(uint32_t deviceid, uint32_t num_bytes) "I3C Dev[%u] send %" PRId32 " bytes to bus" +aspeed_i3c_device_recv_data(uint32_t deviceid, uint32_t num_bytes) "I3C Dev[%u] recv %" PRId32 " bytes from bus" +aspeed_i3c_device_pop_rx(uint32_t deviceid, uint32_t data) "I3C Dev[%u] pop 0x%" PRIx32 " from RX FIFO" +aspeed_i3c_device_resp_queue_push(uint32_t deviceid, uint32_t data) "I3C Dev[%u] push 0x%" PRIx32 " to response queue" +aspeed_i3c_device_push_tx(uint32_t deviceid, uint32_t data) "I3C Dev[%u] push 0x%" PRIx32 " to TX FIFO" +aspeed_i3c_device_pop_tx(uint32_t deviceid, uint32_t data) "I3C Dev[%u] pop 0x%" PRIx32 " from TX FIFO" +aspeed_i3c_device_push_rx(uint32_t deviceid, uint32_t data) "I3C Dev[%u] push 0x%" PRIx32 " to RX FIFO" +aspeed_i3c_device_transfer_ccc(uint32_t deviceid, uint8_t ccc) "I3C Dev[%u] transfer CCC 0x%" PRIx8 +aspeed_i3c_device_cmd_queue_execute(uint32_t deviceid, uint32_t cmd, uint32_t arg) "I3C Dev[%u] execute command 0x%" PRIx32 " arg 0x%" PRIx32 +aspeed_i3c_device_cmd_queue_push(uint32_t deviceid, uint32_t data) "I3C Dev[%u] push 0x%" PRIx32 " to cmd queue" # core.c i3c_target_event(uint8_t address, uint8_t event) "I3C target 0x%" PRIx8 " event 0x%" PRIx8 diff --git a/include/hw/i3c/aspeed_i3c.h b/include/hw/i3c/aspeed_i3c.h index 39679dfa1a..55f2f45915 100644 --- a/include/hw/i3c/aspeed_i3c.h +++ b/include/hw/i3c/aspeed_i3c.h @@ -2,6 +2,7 @@ * ASPEED I3C Controller * * Copyright (C) 2021 ASPEED Technology Inc. + * Copyright (C) 2023 Google, LLC * * This code is licensed under the GPL version 2 or later. See * the COPYING file in the top-level directory. @@ -10,6 +11,8 @@ #ifndef ASPEED_I3C_H #define ASPEED_I3C_H +#include "qemu/fifo32.h" +#include "hw/i3c/i3c.h" #include "hw/sysbus.h" #define TYPE_ASPEED_I3C "aspeed.i3c" @@ -20,6 +23,129 @@ OBJECT_DECLARE_TYPE(AspeedI3CState, AspeedI3CClass, ASPEED_I3C) #define ASPEED_I3C_DEVICE_NR_REGS (0x300 >> 2) #define ASPEED_I3C_NR_DEVICES 6 +#define ASPEED_I3C_CMD_QUEUE_CAPACITY 0x10 +#define ASPEED_I3C_RESP_QUEUE_CAPACITY 0x10 +#define ASPEED_I3C_TX_QUEUE_CAPACITY 0x40 +#define ASPEED_I3C_RX_QUEUE_CAPACITY 0x40 + +/* From datasheet. */ +#define ASPEED_I3C_CMD_ATTR_TRANSFER_CMD 0 +#define ASPEED_I3C_CMD_ATTR_TRANSFER_ARG 1 +#define ASPEED_I3C_CMD_ATTR_SHORT_DATA_ARG 2 +#define ASPEED_I3C_CMD_ATTR_ADDR_ASSIGN_CMD 3 + +/* Enum values from datasheet. */ +typedef enum AspeedI3CRespQueueErr { + ASPEED_I3C_RESP_QUEUE_ERR_NONE = 0, + ASPEED_I3C_RESP_QUEUE_ERR_CRC = 1, + ASPEED_I3C_RESP_QUEUE_ERR_PARITY = 2, + ASPEED_I3C_RESP_QUEUE_ERR_FRAME = 3, + ASPEED_I3C_RESP_QUEUE_ERR_BROADCAST_NACK = 4, + ASPEED_I3C_RESP_QUEUE_ERR_DAA_NACK = 5, + ASPEED_I3C_RESP_QUEUE_ERR_OVERFLOW = 6, + ASPEED_I3C_RESP_QUEUE_ERR_ABORTED = 8, + ASPEED_I3C_RESP_QUEUE_ERR_I2C_NACK = 9, +} AspeedI3CRespQueueErr; + +typedef enum AspeedI3CTransferState { + ASPEED_I3C_TRANSFER_STATE_IDLE = 0x00, + ASPEED_I3C_TRANSFER_STATE_START = 0x01, + ASPEED_I3C_TRANSFER_STATE_RESTART = 0x02, + ASPEED_I3C_TRANSFER_STATE_STOP = 0x03, + ASPEED_I3C_TRANSFER_STATE_START_HOLD = 0x04, + ASPEED_I3C_TRANSFER_STATE_BROADCAST_W = 0x05, + ASPEED_I3C_TRANSFER_STATE_BROADCAST_R = 0x06, + ASPEED_I3C_TRANSFER_STATE_DAA = 0x07, + ASPEED_I3C_TRANSFER_STATE_DAA_GEN = 0x08, + ASPEED_I3C_TRANSFER_STATE_CCC_BYTE = 0x0b, + ASPEED_I3C_TRANSFER_STATE_HDR_CMD = 0x0c, + ASPEED_I3C_TRANSFER_STATE_WRITE = 0x0d, + ASPEED_I3C_TRANSFER_STATE_READ = 0x0e, + ASPEED_I3C_TRANSFER_STATE_IBI_READ = 0x0f, + ASPEED_I3C_TRANSFER_STATE_IBI_DIS = 0x10, + ASPEED_I3C_TRANSFER_STATE_HDR_DDR_CRC = 0x11, + ASPEED_I3C_TRANSFER_STATE_CLK_STRETCH = 0x12, + ASPEED_I3C_TRANSFER_STATE_HALT = 0x13, +} AspeedI3CTransferState; + +typedef enum AspeedI3CTransferStatus { + ASPEED_I3C_TRANSFER_STATUS_IDLE = 0x00, + ASPEED_I3C_TRANSFER_STATUS_BROACAST_CCC = 0x01, + ASPEED_I3C_TRANSFER_STATUS_DIRECT_CCC_W = 0x02, + ASPEED_I3C_TRANSFER_STATUS_DIRECT_CCC_R = 0x03, + ASPEED_I3C_TRANSFER_STATUS_ENTDAA = 0x04, + ASPEED_I3C_TRANSFER_STATUS_SETDASA = 0x05, + ASPEED_I3C_TRANSFER_STATUS_I3C_SDR_W = 0x06, + ASPEED_I3C_TRANSFER_STATUS_I3C_SDR_R = 0x07, + ASPEED_I3C_TRANSFER_STATUS_I2C_SDR_W = 0x08, + ASPEED_I3C_TRANSFER_STATUS_I2C_SDR_R = 0x09, + ASPEED_I3C_TRANSFER_STATUS_HDR_TS_W = 0x0a, + ASPEED_I3C_TRANSFER_STATUS_HDR_TS_R = 0x0b, + ASPEED_I3C_TRANSFER_STATUS_HDR_DDR_W = 0x0c, + ASPEED_I3C_TRANSFER_STATUS_HDR_DDR_R = 0x0d, + ASPEED_I3C_TRANSFER_STATUS_IBI = 0x0e, + ASPEED_I3C_TRANSFER_STATUS_HALT = 0x0f, +} AspeedI3CTransferStatus; + +/* + * Transfer commands and arguments are 32-bit wide values that the user passes + * into the command queue. We interpret each 32-bit word based on the cmd_attr + * field. + */ +typedef struct AspeedI3CTransferCmd { + uint8_t cmd_attr:3; + uint8_t tid:4; /* Transaction ID */ + uint16_t cmd:8; + uint8_t cp:1; /* Command present */ + uint8_t dev_index:5; + uint8_t speed:3; + uint8_t resv0:1; + uint8_t dbp:1; /* Defining byte present */ + uint8_t roc:1; /* Response on completion */ + uint8_t sdap:1; /* Short data argument present */ + uint8_t rnw:1; /* Read not write */ + uint8_t resv1:1; + uint8_t toc:1; /* Termination (I3C STOP) on completion */ + uint8_t pec:1; /* Parity error check enabled */ +} AspeedI3CTransferCmd; + +typedef struct AspeedI3CTransferArg { + uint8_t cmd_attr:3; + uint8_t resv:5; + uint8_t db; /* Defining byte */ + uint16_t data_len; +} AspeedI3CTransferArg; + +typedef struct AspeedI3CShortArg { + uint8_t cmd_attr:3; + uint8_t byte_strb:3; + uint8_t resv:2; + uint8_t byte0; + uint8_t byte1; + uint8_t byte2; +} AspeedI3CShortArg; + +typedef struct AspeedI3CAddrAssignCmd { + uint8_t cmd_attr:3; + uint8_t tid:4; /* Transaction ID */ + uint16_t cmd:8; + uint8_t resv0:1; + uint8_t dev_index:5; + uint16_t dev_count:5; + uint8_t roc:1; /* Response on completion */ + uint8_t resv1:3; + uint8_t toc:1; /* Termination (I3C STOP) on completion */ + uint8_t resv2:1; +} AspeedI3CAddrAssignCmd; + +typedef union AspeedI3CCmdQueueData { + uint32_t word; + AspeedI3CTransferCmd transfer_cmd; + AspeedI3CTransferArg transfer_arg; + AspeedI3CShortArg short_arg; + AspeedI3CAddrAssignCmd addr_assign_cmd; +} AspeedI3CCmdQueueData; + OBJECT_DECLARE_SIMPLE_TYPE(AspeedI3CDevice, ASPEED_I3C_DEVICE) typedef struct AspeedI3CDevice { /* */ @@ -28,6 +154,12 @@ typedef struct AspeedI3CDevice { /* */ MemoryRegion mr; qemu_irq irq; + I3CBus *bus; + + Fifo32 cmd_queue; + Fifo32 resp_queue; + Fifo32 tx_queue; + Fifo32 rx_queue; uint8_t id; uint32_t regs[ASPEED_I3C_DEVICE_NR_REGS]; From patchwork Fri Mar 31 01:01:25 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Joe Komlodi X-Patchwork-Id: 1763559 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=nongnu.org (client-ip=209.51.188.17; helo=lists.gnu.org; envelope-from=qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org; receiver=) Authentication-Results: legolas.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=google.com header.i=@google.com header.a=rsa-sha256 header.s=20210112 header.b=GZ7c65bu; dkim-atps=neutral Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-ECDSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4Pnhsj6l59z1yZ4 for ; Fri, 31 Mar 2023 12:05:13 +1100 (AEDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1pi39Q-0003FX-Hr; Thu, 30 Mar 2023 21:01:56 -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 <3_TAmZAcKCiAGKIHK9ECKKCHA.8KIMAIQ-9ARAHJKJCJQ.KNC@flex--komlodi.bounces.google.com>) id 1pi39O-0003Ed-9d for qemu-devel@nongnu.org; Thu, 30 Mar 2023 21:01:54 -0400 Received: from mail-pf1-x449.google.com ([2607:f8b0:4864:20::449]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from <3_TAmZAcKCiAGKIHK9ECKKCHA.8KIMAIQ-9ARAHJKJCJQ.KNC@flex--komlodi.bounces.google.com>) id 1pi39L-0006bu-Sr for qemu-devel@nongnu.org; Thu, 30 Mar 2023 21:01:54 -0400 Received: by mail-pf1-x449.google.com with SMTP id x68-20020a628647000000b0062624c52117so9614343pfd.14 for ; Thu, 30 Mar 2023 18:01:50 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20210112; t=1680224510; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=aVvvr1bYSbeJf4UIVyQB9FV1A2yFNqaW2LAnOy/KAXw=; b=GZ7c65buXflW3TRzzcvlU/Nu2HlZDeNnvr92DE8HoWo9WE6H/O4gBrJ1J+s3QFZJUC ExLFdpJPqBghVv995pGVpajEw/p9l4RpAX3cvoYHRFvdcdu2Vvb469zLy3kLFbYxPHZL rMCbfVMvyh0HrHUPJDj0Kk0AV1tka2mMF0b9no8HC+ig1haOPYi7C7ce6btrWZSAM4m7 Ht+mPQJhJr08ZpGksYi+Yy8MJsmfbiIZu3U8Uh4o6OHEFpdkO0Eqf9rTaND9eWWAIket 6OmdhwSEPyuY9WZDROBhpC84ieen+QmMaT4NWKeagdKk/JuK/1FrfBZ7ryH3ltcsnmSG 1MoQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; t=1680224510; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=aVvvr1bYSbeJf4UIVyQB9FV1A2yFNqaW2LAnOy/KAXw=; b=2djKR9+wgIAneq9r3NA/uBCW3YCjJ29cJCJsGEZzpxR5fM/sIOyzzDrXW9e8XMFdxm t1T9KC7rZS5+w/v8mClApojtkVCBdDz2Efrfh4zBjafVA2YrwhGKrsh1njokhJJXJDUn TdDJD2DBMLmKDwsjqsxOZrQwr8YeqKmMykq8ZhezdrVSXfET88gZ6aptzUBVnScD+0aX zp4cetw7lA13Sm+jqFYhaJGJJve+4YzeFxHR/pCqkY6udVVviWheI8SSmHtUvFKXoVSh AIYReS9mMKfgzoWqtQ5hvqQQMIHmQoYEDQDOU7YOYVa2JyKPT+gHJReGjyx6l5iXnC+4 049g== X-Gm-Message-State: AAQBX9fzzMu9n0FFYjrrc9tAjlqOCb5kFxek3tAHgK/5yD7nyzA8WZJB SQVHjg8fPU8Rm/pPRZWPbSc7mEowkxjJOSGrscNKyIxaCREulH38UkjW/gLOG+FGdjTzcIWd/3y AmuYmXsDs2ntM5wYqwRAwp9ytya5879R4dcDvPTSPPQqWgmnsd9Fgi4KQ/3cNa8M= X-Google-Smtp-Source: AKy350ZYiPTKMX0AnCDKSH/qZqy0Sv7N3Hinb/TdIut1yXIjQZ4xIkoZ03V0S5RSNbyDGVu8EvineD9hdj/W X-Received: from komlodi.c.googlers.com ([fda3:e722:ac3:cc00:7f:e700:c0a8:35ee]) (user=komlodi job=sendgmr) by 2002:a63:dc0c:0:b0:50b:dca1:b6f9 with SMTP id s12-20020a63dc0c000000b0050bdca1b6f9mr6920390pgg.1.1680224509998; Thu, 30 Mar 2023 18:01:49 -0700 (PDT) Date: Fri, 31 Mar 2023 01:01:25 +0000 In-Reply-To: <20230331010131.1412571-1-komlodi@google.com> Mime-Version: 1.0 References: <20230331010131.1412571-1-komlodi@google.com> X-Mailer: git-send-email 2.40.0.348.gf938b09366-goog Message-ID: <20230331010131.1412571-11-komlodi@google.com> Subject: [PATCH 10/16] hw/i3c/aspeed_i3c: Add IBI handling From: Joe Komlodi To: qemu-devel@nongnu.org Cc: venture@google.com, komlodi@google.com, peter.maydell@linaro.org Received-SPF: pass client-ip=2607:f8b0:4864:20::449; envelope-from=3_TAmZAcKCiAGKIHK9ECKKCHA.8KIMAIQ-9ARAHJKJCJQ.KNC@flex--komlodi.bounces.google.com; helo=mail-pf1-x449.google.com X-Spam_score_int: -95 X-Spam_score: -9.6 X-Spam_bar: --------- X-Spam_report: (-9.6 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_MED=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, USER_IN_DEF_DKIM_WL=-7.5 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+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Adds handling for different IBI events that the controller can receive. This includes: - Handling a hot-join from a target - Handling a secondary controller on the bus requesting to be the primary bus controller - Handling an interrupt request from a target. When receiving an IBI, the controller sets an interrupt to notify software about what happened. When the IBI is finished being serviced, the controller pushes the result of the IBI and any data received from the target into the IBI queue. Signed-off-by: Joe Komlodi Reviewed-by: Patrick Venture Reviewed-by: Stephen Longfield --- hw/i3c/aspeed_i3c.c | 310 ++++++++++++++++++++++++++++++++++++ hw/i3c/trace-events | 2 + include/hw/i3c/aspeed_i3c.h | 27 ++++ 3 files changed, 339 insertions(+) diff --git a/hw/i3c/aspeed_i3c.c b/hw/i3c/aspeed_i3c.c index ffe0b21ca0..055ad3f7fd 100644 --- a/hw/i3c/aspeed_i3c.c +++ b/hw/i3c/aspeed_i3c.c @@ -20,6 +20,14 @@ #include "hw/i3c/i3c.h" #include "hw/irq.h" +/* + * Disable event command values. sent along with a DISEC CCC to disable certain + * events on targets. + */ +#define DISEC_HJ 0x08 +#define DISEC_CR 0x02 +#define DISEC_INT 0x01 + /* I3C Controller Registers */ REG32(I3C1_REG0, 0x10) REG32(I3C1_REG1, 0x14) @@ -444,6 +452,23 @@ static inline uint8_t aspeed_i3c_device_fifo_threshold_from_reg(uint8_t regval) return regval = regval ? (2 << regval) : 1; } +static inline uint8_t aspeed_i3c_device_ibi_slice_size(AspeedI3CDevice *s) +{ + uint8_t ibi_slice_size = ARRAY_FIELD_EX32(s->regs, QUEUE_THLD_CTRL, + IBI_DATA_THLD); + /* The minimum supported slice size is 4 bytes. */ + if (ibi_slice_size == 0) { + ibi_slice_size = 1; + } + ibi_slice_size *= sizeof(uint32_t); + /* maximum supported size is 63 bytes. */ + if (ibi_slice_size >= 64) { + ibi_slice_size = 63; + } + + return ibi_slice_size; +} + static void aspeed_i3c_device_update_irq(AspeedI3CDevice *s) { bool level = !!(s->regs[R_INTR_SIGNAL_EN] & s->regs[R_INTR_STATUS]); @@ -591,6 +616,261 @@ static uint8_t aspeed_i3c_device_target_addr(AspeedI3CDevice *s, DEV_DYNAMIC_ADDR); } +static int aspeed_i3c_device_addr_table_index_from_addr(AspeedI3CDevice *s, + uint8_t addr) +{ + uint8_t table_size = ARRAY_FIELD_EX32(s->regs, DEVICE_ADDR_TABLE_POINTER, + DEPTH); + for (uint8_t i = 0; i < table_size; i++) { + if (aspeed_i3c_device_target_addr(s, i) == addr) { + return i; + } + } + return -1; +} + +static void aspeed_i3c_device_send_disec(AspeedI3CDevice *s) +{ + uint8_t ccc = I3C_CCC_DISEC; + if (s->ibi_data.send_direct_disec) { + ccc = I3C_CCCD_DISEC; + } + + aspeed_i3c_device_send_start(s, I3C_BROADCAST, /*is_recv=*/false, + /*is_i2c=*/false); + aspeed_i3c_device_send_byte(s, ccc, /*is_i2c=*/false); + if (s->ibi_data.send_direct_disec) { + aspeed_i3c_device_send_start(s, s->ibi_data.disec_addr, + /*is_recv=*/false, /*is_i2c=*/false); + } + aspeed_i3c_device_send_byte(s, s->ibi_data.disec_byte, /*is_i2c=*/false); +} + +static int aspeed_i3c_device_handle_hj(AspeedI3CDevice *s) +{ + if (ARRAY_FIELD_EX32(s->regs, IBI_QUEUE_CTRL, NOTIFY_REJECTED_HOT_JOIN)) { + s->ibi_data.notify_ibi_nack = true; + } + + bool nack_and_disable = ARRAY_FIELD_EX32(s->regs, DEVICE_CTRL, + HOT_JOIN_ACK_NACK_CTRL); + if (nack_and_disable) { + s->ibi_data.ibi_queue_status = FIELD_DP32(s->ibi_data.ibi_queue_status, + IBI_QUEUE_STATUS, + IBI_STATUS, 1); + s->ibi_data.ibi_nacked = true; + s->ibi_data.disec_byte = DISEC_HJ; + return -1; + } + return 0; +} + +static int aspeed_i3c_device_handle_ctlr_req(AspeedI3CDevice *s, uint8_t addr) +{ + if (ARRAY_FIELD_EX32(s->regs, IBI_QUEUE_CTRL, NOTIFY_REJECTED_MASTER_REQ)) { + s->ibi_data.notify_ibi_nack = true; + } + + int table_offset = aspeed_i3c_device_addr_table_index_from_addr(s, addr); + /* Doesn't exist in the table, NACK it, don't DISEC. */ + if (table_offset < 0) { + return -1; + } + + table_offset += R_DEVICE_ADDR_TABLE_LOC1; + if (FIELD_EX32(s->regs[table_offset], DEVICE_ADDR_TABLE_LOC1, MR_REJECT)) { + s->ibi_data.ibi_queue_status = FIELD_DP32(s->ibi_data.ibi_queue_status, + IBI_QUEUE_STATUS, + IBI_STATUS, 1); + s->ibi_data.ibi_nacked = true; + s->ibi_data.disec_addr = addr; + /* Tell the requester to disable controller role requests. */ + s->ibi_data.disec_byte = DISEC_CR; + s->ibi_data.send_direct_disec = true; + return -1; + } + return 0; +} + +static int aspeed_i3c_device_handle_targ_irq(AspeedI3CDevice *s, uint8_t addr) +{ + if (ARRAY_FIELD_EX32(s->regs, IBI_QUEUE_CTRL, NOTIFY_REJECTED_SLAVE_IRQ)) { + s->ibi_data.notify_ibi_nack = true; + } + + int table_offset = aspeed_i3c_device_addr_table_index_from_addr(s, addr); + /* Doesn't exist in the table, NACK it, don't DISEC. */ + if (table_offset < 0) { + return -1; + } + + table_offset += R_DEVICE_ADDR_TABLE_LOC1; + if (FIELD_EX32(s->regs[table_offset], DEVICE_ADDR_TABLE_LOC1, SIR_REJECT)) { + s->ibi_data.ibi_queue_status = FIELD_DP32(s->ibi_data.ibi_queue_status, + IBI_QUEUE_STATUS, + IBI_STATUS, 1); + s->ibi_data.ibi_nacked = true; + s->ibi_data.disec_addr = addr; + /* Tell the requester to disable interrupts. */ + s->ibi_data.disec_byte = DISEC_INT; + s->ibi_data.send_direct_disec = true; + return -1; + } + return 0; +} + +static int aspeed_i3c_device_ibi_handle(I3CBus *bus, I3CTarget *target, + uint8_t addr, bool is_recv) +{ + AspeedI3CDevice *s = ASPEED_I3C_DEVICE(bus->qbus.parent); + + trace_aspeed_i3c_device_ibi_handle(s->id, addr, is_recv); + s->ibi_data.ibi_queue_status = FIELD_DP32(s->ibi_data.ibi_queue_status, + IBI_QUEUE_STATUS, IBI_ID, + (addr << 1) | is_recv); + /* Is this a hot join request? */ + if (addr == I3C_HJ_ADDR) { + return aspeed_i3c_device_handle_hj(s); + } + /* Is secondary controller requesting access? */ + if (addr == target->address && !is_recv) { + return aspeed_i3c_device_handle_ctlr_req(s, addr); + } + /* Is this a target IRQ? */ + if (addr == target->address && is_recv) { + return aspeed_i3c_device_handle_targ_irq(s, addr); + } + + /* Not sure what this is, NACK it. */ + return -1; +} + +static int aspeed_i3c_device_ibi_recv(I3CBus *bus, uint8_t data) +{ + AspeedI3CDevice *s = ASPEED_I3C_DEVICE(bus->qbus.parent); + if (fifo8_is_full(&s->ibi_data.ibi_intermediate_queue)) { + return -1; + } + + fifo8_push(&s->ibi_data.ibi_intermediate_queue, data); + trace_aspeed_i3c_device_ibi_recv(s->id, data); + return 0; +} + +static void aspeed_i3c_device_ibi_queue_push(AspeedI3CDevice *s) +{ + /* Stored value is in 32-bit chunks, convert it to byte chunks. */ + uint8_t ibi_slice_size = aspeed_i3c_device_ibi_slice_size(s); + uint8_t num_slices = fifo8_num_used(&s->ibi_data.ibi_intermediate_queue) / + ibi_slice_size; + uint8_t ibi_status_count = num_slices; + union { + uint8_t b[sizeof(uint32_t)]; + uint32_t val32; + } ibi_data = { + .val32 = 0 + }; + + /* The report was suppressed, do nothing. */ + if (s->ibi_data.ibi_nacked && !s->ibi_data.notify_ibi_nack) { + ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_ST_STATUS, + ASPEED_I3C_TRANSFER_STATE_IDLE); + ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_STATUS, + ASPEED_I3C_TRANSFER_STATUS_IDLE); + return; + } + + /* If we don't have any slices to push, just push the status. */ + if (num_slices == 0) { + s->ibi_data.ibi_queue_status = + FIELD_DP32(s->ibi_data.ibi_queue_status, IBI_QUEUE_STATUS, + LAST_STATUS, 1); + fifo32_push(&s->ibi_queue, s->ibi_data.ibi_queue_status); + ibi_status_count = 1; + } + + for (uint8_t i = 0; i < num_slices; i++) { + /* If this is the last slice, set LAST_STATUS. */ + if (fifo8_num_used(&s->ibi_data.ibi_intermediate_queue) < + ibi_slice_size) { + s->ibi_data.ibi_queue_status = + FIELD_DP32(s->ibi_data.ibi_queue_status, IBI_QUEUE_STATUS, + IBI_DATA_LEN, + fifo8_num_used(&s->ibi_data.ibi_intermediate_queue)); + s->ibi_data.ibi_queue_status = + FIELD_DP32(s->ibi_data.ibi_queue_status, IBI_QUEUE_STATUS, + LAST_STATUS, 1); + } else { + s->ibi_data.ibi_queue_status = + FIELD_DP32(s->ibi_data.ibi_queue_status, IBI_QUEUE_STATUS, + IBI_DATA_LEN, ibi_slice_size); + } + + /* Push the IBI status header. */ + fifo32_push(&s->ibi_queue, s->ibi_data.ibi_queue_status); + /* Move each IBI byte into a 32-bit word and push it into the queue. */ + for (uint8_t j = 0; j < ibi_slice_size; ++j) { + if (fifo8_is_empty(&s->ibi_data.ibi_intermediate_queue)) { + break; + } + + ibi_data.b[j & 3] = fifo8_pop(&s->ibi_data.ibi_intermediate_queue); + /* We have 32-bits, push it to the IBI FIFO. */ + if ((j & 0x03) == 0x03) { + fifo32_push(&s->ibi_queue, ibi_data.val32); + ibi_data.val32 = 0; + } + } + /* If the data isn't 32-bit aligned, push the leftover bytes. */ + if (ibi_slice_size & 0x03) { + fifo32_push(&s->ibi_queue, ibi_data.val32); + } + + /* Clear out the data length for the next iteration. */ + s->ibi_data.ibi_queue_status = FIELD_DP32(s->ibi_data.ibi_queue_status, + IBI_QUEUE_STATUS, IBI_DATA_LEN, 0); + } + + ARRAY_FIELD_DP32(s->regs, QUEUE_STATUS_LEVEL, IBI_BUF_BLR, + fifo32_num_used(&s->ibi_queue)); + ARRAY_FIELD_DP32(s->regs, QUEUE_STATUS_LEVEL, IBI_STATUS_CNT, + ibi_status_count); + /* Threshold is the register value + 1. */ + uint8_t threshold = ARRAY_FIELD_EX32(s->regs, QUEUE_THLD_CTRL, + IBI_STATUS_THLD) + 1; + if (fifo32_num_used(&s->ibi_queue) >= threshold) { + ARRAY_FIELD_DP32(s->regs, INTR_STATUS, IBI_THLD, 1); + aspeed_i3c_device_update_irq(s); + } + + /* State update. */ + ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_ST_STATUS, + ASPEED_I3C_TRANSFER_STATE_IDLE); + ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_STATUS, + ASPEED_I3C_TRANSFER_STATUS_IDLE); +} + +static int aspeed_i3c_device_ibi_finish(I3CBus *bus) +{ + AspeedI3CDevice *s = ASPEED_I3C_DEVICE(bus->qbus.parent); + bool nack_and_disable_hj = ARRAY_FIELD_EX32(s->regs, DEVICE_CTRL, + HOT_JOIN_ACK_NACK_CTRL); + if (nack_and_disable_hj || s->ibi_data.send_direct_disec) { + aspeed_i3c_device_send_disec(s); + } + aspeed_i3c_device_ibi_queue_push(s); + + /* Clear out the intermediate values. */ + s->ibi_data.ibi_queue_status = 0; + s->ibi_data.disec_addr = 0; + s->ibi_data.disec_byte = 0; + s->ibi_data.send_direct_disec = false; + s->ibi_data.notify_ibi_nack = false; + s->ibi_data.ibi_nacked = false; + + return 0; +} + static uint32_t aspeed_i3c_device_intr_status_r(AspeedI3CDevice *s) { /* Only return the status whose corresponding EN bits are set. */ @@ -650,6 +930,25 @@ static uint32_t aspeed_i3c_device_pop_rx(AspeedI3CDevice *s) return val; } +static uint32_t aspeed_i3c_device_ibi_queue_r(AspeedI3CDevice *s) +{ + if (fifo32_is_empty(&s->ibi_queue)) { + return 0; + } + + uint32_t val = fifo32_pop(&s->ibi_queue); + ARRAY_FIELD_DP32(s->regs, QUEUE_STATUS_LEVEL, IBI_BUF_BLR, + fifo32_num_used(&s->ibi_queue)); + /* Threshold is the register value + 1. */ + uint8_t threshold = ARRAY_FIELD_EX32(s->regs, QUEUE_THLD_CTRL, + IBI_STATUS_THLD) + 1; + if (fifo32_num_used(&s->ibi_queue) < threshold) { + ARRAY_FIELD_DP32(s->regs, INTR_STATUS, IBI_THLD, 0); + aspeed_i3c_device_update_irq(s); + } + return val; +} + static uint32_t aspeed_i3c_device_resp_queue_port_r(AspeedI3CDevice *s) { if (fifo32_is_empty(&s->resp_queue)) { @@ -687,6 +986,9 @@ static uint64_t aspeed_i3c_device_read(void *opaque, hwaddr offset, case R_INTR_FORCE: value = 0; break; + case R_IBI_QUEUE_DATA: + value = aspeed_i3c_device_ibi_queue_r(s); + break; case R_INTR_STATUS: value = aspeed_i3c_device_intr_status_r(s); break; @@ -1412,8 +1714,16 @@ static void aspeed_i3c_device_realize(DeviceState *dev, Error **errp) fifo32_create(&s->resp_queue, ASPEED_I3C_RESP_QUEUE_CAPACITY); fifo32_create(&s->tx_queue, ASPEED_I3C_TX_QUEUE_CAPACITY); fifo32_create(&s->rx_queue, ASPEED_I3C_RX_QUEUE_CAPACITY); + fifo32_create(&s->ibi_queue, ASPEED_I3C_IBI_QUEUE_CAPACITY); + /* Arbitrarily large enough to not be an issue. */ + fifo8_create(&s->ibi_data.ibi_intermediate_queue, + ASPEED_I3C_IBI_QUEUE_CAPACITY * 8); s->bus = i3c_init_bus(DEVICE(s), name); + I3CBusClass *bc = I3C_BUS_GET_CLASS(s->bus); + bc->ibi_handle = aspeed_i3c_device_ibi_handle; + bc->ibi_recv = aspeed_i3c_device_ibi_recv; + bc->ibi_finish = aspeed_i3c_device_ibi_finish; } static uint64_t aspeed_i3c_read(void *opaque, hwaddr addr, unsigned int size) diff --git a/hw/i3c/trace-events b/hw/i3c/trace-events index 370c08ea45..7a202b28c9 100644 --- a/hw/i3c/trace-events +++ b/hw/i3c/trace-events @@ -7,6 +7,8 @@ aspeed_i3c_device_read(uint32_t deviceid, uint64_t offset, uint64_t data) "I3C D aspeed_i3c_device_write(uint32_t deviceid, uint64_t offset, uint64_t data) "I3C Dev[%u] write: offset 0x%" PRIx64 " data 0x%" PRIx64 aspeed_i3c_device_send(uint32_t deviceid, uint32_t num_bytes) "I3C Dev[%u] send %" PRId32 " bytes to bus" aspeed_i3c_device_recv_data(uint32_t deviceid, uint32_t num_bytes) "I3C Dev[%u] recv %" PRId32 " bytes from bus" +aspeed_i3c_device_ibi_recv(uint32_t deviceid, uint8_t ibi_byte) "I3C Dev[%u] recv IBI byte 0x%" PRIx8 +aspeed_i3c_device_ibi_handle(uint32_t deviceid, uint8_t addr, bool rnw) "I3C Dev[%u] handle IBI from address 0x%" PRIx8 " RnW=%d" aspeed_i3c_device_pop_rx(uint32_t deviceid, uint32_t data) "I3C Dev[%u] pop 0x%" PRIx32 " from RX FIFO" aspeed_i3c_device_resp_queue_push(uint32_t deviceid, uint32_t data) "I3C Dev[%u] push 0x%" PRIx32 " to response queue" aspeed_i3c_device_push_tx(uint32_t deviceid, uint32_t data) "I3C Dev[%u] push 0x%" PRIx32 " to TX FIFO" diff --git a/include/hw/i3c/aspeed_i3c.h b/include/hw/i3c/aspeed_i3c.h index 55f2f45915..d2f0739b79 100644 --- a/include/hw/i3c/aspeed_i3c.h +++ b/include/hw/i3c/aspeed_i3c.h @@ -27,6 +27,7 @@ OBJECT_DECLARE_TYPE(AspeedI3CState, AspeedI3CClass, ASPEED_I3C) #define ASPEED_I3C_RESP_QUEUE_CAPACITY 0x10 #define ASPEED_I3C_TX_QUEUE_CAPACITY 0x40 #define ASPEED_I3C_RX_QUEUE_CAPACITY 0x40 +#define ASPEED_I3C_IBI_QUEUE_CAPACITY 0x10 /* From datasheet. */ #define ASPEED_I3C_CMD_ATTR_TRANSFER_CMD 0 @@ -146,6 +147,28 @@ typedef union AspeedI3CCmdQueueData { AspeedI3CAddrAssignCmd addr_assign_cmd; } AspeedI3CCmdQueueData; +/* + * When we receive an IBI with data, we need to store it temporarily until + * the target is finished sending data. Then we can set the IBI queue status + * appropriately. + */ +typedef struct AspeedI3CDeviceIBIData { + /* Do we notify the user that an IBI was NACKed? */ + bool notify_ibi_nack; + /* Intermediate storage of IBI_QUEUE_STATUS. */ + uint32_t ibi_queue_status; + /* Temporary buffer to store IBI data from the target. */ + Fifo8 ibi_intermediate_queue; + /* The address we should send a CCC_DISEC to. */ + uint8_t disec_addr; + /* The byte we should send along with the CCC_DISEC. */ + uint8_t disec_byte; + /* Should we send a direct DISEC CCC? (As opposed to global). */ + bool send_direct_disec; + /* Was this IBI NACKed? */ + bool ibi_nacked; +} AspeedI3CDeviceIBIData; + OBJECT_DECLARE_SIMPLE_TYPE(AspeedI3CDevice, ASPEED_I3C_DEVICE) typedef struct AspeedI3CDevice { /* */ @@ -160,6 +183,10 @@ typedef struct AspeedI3CDevice { Fifo32 resp_queue; Fifo32 tx_queue; Fifo32 rx_queue; + Fifo32 ibi_queue; + + /* Temporary storage for IBI data. */ + AspeedI3CDeviceIBIData ibi_data; uint8_t id; uint32_t regs[ASPEED_I3C_DEVICE_NR_REGS]; From patchwork Fri Mar 31 01:01:26 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Joe Komlodi X-Patchwork-Id: 1763555 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=nongnu.org (client-ip=209.51.188.17; helo=lists.gnu.org; envelope-from=qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org; receiver=) Authentication-Results: legolas.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=google.com header.i=@google.com header.a=rsa-sha256 header.s=20210112 header.b=jt4DXKXc; dkim-atps=neutral Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-ECDSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4PnhsX4k0Gz1yYr for ; Fri, 31 Mar 2023 12:05:04 +1100 (AEDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1pi39R-0003Fl-Ay; Thu, 30 Mar 2023 21:01:57 -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 <3_zAmZAcKCiIIMKJMBGEMMEJC.AMKOCKS-BCTCJLMLELS.MPE@flex--komlodi.bounces.google.com>) id 1pi39P-0003F1-4j for qemu-devel@nongnu.org; Thu, 30 Mar 2023 21:01:55 -0400 Received: from mail-pl1-x649.google.com ([2607:f8b0:4864:20::649]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from <3_zAmZAcKCiIIMKJMBGEMMEJC.AMKOCKS-BCTCJLMLELS.MPE@flex--komlodi.bounces.google.com>) id 1pi39N-0006by-A0 for qemu-devel@nongnu.org; Thu, 30 Mar 2023 21:01:54 -0400 Received: by mail-pl1-x649.google.com with SMTP id z16-20020a170902d55000b001a06f9b5e31so12297588plf.21 for ; Thu, 30 Mar 2023 18:01:52 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20210112; t=1680224511; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=z9daf8epV3wtDyZD1agoFY4Syp6h/vNC0BTkpI5/9/A=; b=jt4DXKXc7jdVUZE1N8eMgT1Z6F64+BFuReMETxbBaGlFkD16Locc22TTP98EzHGHGT S/f0iTK1TISEknUPonuuV1EY1o9vmTWt7C2h0WyltPHqgajVQcgVCMH1V5mw40jL1XXD rC0nSor4/qNV6skNVUI78w4X1HjGUIUK8Jh0Z+8/Zyqd8ytqukmz7pVBe7PGCMF6qxRS 9pVjesHiAWMIA/zpjyv+CvbsYy3aXK++XQGqr6evk0T1+p8RLZtpb2PL8WMwLAIGzMiu Aso8M0HXihVVxAZMFljEPoNAE4mni+6lNiikBbrmukSpIyVpPbJ90bcW/L7Q/eN4e9K7 AuFg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; t=1680224511; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=z9daf8epV3wtDyZD1agoFY4Syp6h/vNC0BTkpI5/9/A=; b=RdmcBFlzs8O/tOlJ7BvezrTCRe9eimZyWwGrgfiJfx2Q0OshG1Zw6cezoXp63EKa8N GQDWlNuBk8Zz8IOgGnEmwQz/ugwwOBovz9AbfMp0O/OJTNzk7WpsIiyKSILxv9iI3x4A acJYUeFwM40hDgvSbcEJpMMd/y1BCaj6Zxhet2ipx8jQlYfOeQ7pTgIhZBzzo6G5mwv2 tfCMCxQ9iZl6wRSdjlIBbyglBTfWeZxB/dd9+eMrg5Vkcok/k8uJyz++6SnVXGoNGWpR gxpiI1iN/LZuf8chmTK5h1RoVXKi/PvqR1ijGvbXM5M1RcVbMN5s3juIHHy7Am5J9Jx4 Mvng== X-Gm-Message-State: AAQBX9crlVUtaKKmBvVkWQAyKx1bi1HAIwNw1f8uhlanJKOnGUCJ7HCH Ay2n5LiGi/noRwNSttLY6JYhdirDVssL9Zd/MCN3DmHIladXzeI/4slX3Ga3LXn/i8Yb0TFzx7i LZZ5uLpT8Nvf/IEOhO4HcSnPi2GMYplBdrMLSxvDyEM3AtYcK16p9fg3Pj3dx00Q= X-Google-Smtp-Source: AKy350ajrU+uwpm0u2ajBH5dwdp5V8EdIIFbxnuvDcfN5QLkP7jFt9tDpR+lXVyRzhE+BewoOKTRZclQyyK8 X-Received: from komlodi.c.googlers.com ([fda3:e722:ac3:cc00:7f:e700:c0a8:35ee]) (user=komlodi job=sendgmr) by 2002:a05:6a00:a1a:b0:62d:9b70:1afd with SMTP id p26-20020a056a000a1a00b0062d9b701afdmr3616121pfh.1.1680224511502; Thu, 30 Mar 2023 18:01:51 -0700 (PDT) Date: Fri, 31 Mar 2023 01:01:26 +0000 In-Reply-To: <20230331010131.1412571-1-komlodi@google.com> Mime-Version: 1.0 References: <20230331010131.1412571-1-komlodi@google.com> X-Mailer: git-send-email 2.40.0.348.gf938b09366-goog Message-ID: <20230331010131.1412571-12-komlodi@google.com> Subject: [PATCH 11/16] hw/i3c/aspeed_i3c: Add ctrl MMIO handling From: Joe Komlodi To: qemu-devel@nongnu.org Cc: venture@google.com, komlodi@google.com, peter.maydell@linaro.org Received-SPF: pass client-ip=2607:f8b0:4864:20::649; envelope-from=3_zAmZAcKCiIIMKJMBGEMMEJC.AMKOCKS-BCTCJLMLELS.MPE@flex--komlodi.bounces.google.com; helo=mail-pl1-x649.google.com X-Spam_score_int: -95 X-Spam_score: -9.6 X-Spam_bar: --------- X-Spam_report: (-9.6 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_MED=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, USER_IN_DEF_DKIM_WL=-7.5 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+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Adds functionality to the CTRL register. Signed-off-by: Joe Komlodi Reviewed-by: Titus Rwantare Reviewed-by: Patrick Venture --- hw/i3c/aspeed_i3c.c | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/hw/i3c/aspeed_i3c.c b/hw/i3c/aspeed_i3c.c index 055ad3f7fd..4c0c074012 100644 --- a/hw/i3c/aspeed_i3c.c +++ b/hw/i3c/aspeed_i3c.c @@ -340,6 +340,8 @@ REG32(DEVICE_ADDR_TABLE_LOC1, 0x280) FIELD(DEVICE_ADDR_TABLE_LOC1, DEV_NACK_RETRY_CNT, 29, 2) FIELD(DEVICE_ADDR_TABLE_LOC1, LEGACY_I2C_DEVICE, 31, 1) +static void aspeed_i3c_device_cmd_queue_execute(AspeedI3CDevice *s); + static const uint32_t ast2600_i3c_controller_ro[ASPEED_I3C_DEVICE_NR_REGS] = { [R_I3C1_REG0] = 0xfc000000, [R_I3C1_REG1] = 0xfff00000, @@ -588,6 +590,37 @@ static int aspeed_i3c_device_recv_data(AspeedI3CDevice *s, bool is_i2c, return ret; } +static inline void aspeed_i3c_device_ctrl_w(AspeedI3CDevice *s, + uint32_t val) +{ + /* + * If the user is setting I3C_RESUME, the controller was halted. + * Try and resume execution and leave the bit cleared. + */ + if (FIELD_EX32(val, DEVICE_CTRL, I3C_RESUME)) { + aspeed_i3c_device_cmd_queue_execute(s); + val = FIELD_DP32(val, DEVICE_CTRL, I3C_RESUME, 0); + } + /* + * I3C_ABORT being set sends an I3C STOP. It's cleared when the STOP is + * sent. + */ + if (FIELD_EX32(val, DEVICE_CTRL, I3C_ABORT)) { + aspeed_i3c_device_end_transfer(s, /*is_i2c=*/true); + aspeed_i3c_device_end_transfer(s, /*is_i2c=*/false); + val = FIELD_DP32(val, DEVICE_CTRL, I3C_ABORT, 0); + ARRAY_FIELD_DP32(s->regs, INTR_STATUS, TRANSFER_ABORT, 1); + aspeed_i3c_device_update_irq(s); + } + /* Update present state. */ + ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_ST_STATUS, + ASPEED_I3C_TRANSFER_STATE_IDLE); + ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_STATUS, + ASPEED_I3C_TRANSFER_STATUS_IDLE); + + s->regs[R_DEVICE_CTRL] = val; +} + static inline bool aspeed_i3c_device_target_is_i2c(AspeedI3CDevice *s, uint16_t offset) { @@ -1650,6 +1683,9 @@ static void aspeed_i3c_device_write(void *opaque, hwaddr offset, "] = 0x%08" PRIx64 "\n", __func__, offset, value); break; + case R_DEVICE_CTRL: + aspeed_i3c_device_ctrl_w(s, val32); + break; case R_RX_TX_DATA_PORT: aspeed_i3c_device_push_tx(s, val32); break; From patchwork Fri Mar 31 01:01:27 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Joe Komlodi X-Patchwork-Id: 1763557 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=nongnu.org (client-ip=209.51.188.17; helo=lists.gnu.org; envelope-from=qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org; receiver=) Authentication-Results: legolas.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=google.com header.i=@google.com header.a=rsa-sha256 header.s=20210112 header.b=b4G2rzgA; dkim-atps=neutral Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-ECDSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4Pnhsh60Gdz1yY8 for ; Fri, 31 Mar 2023 12:05:12 +1100 (AEDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1pi39R-0003G0-Vt; Thu, 30 Mar 2023 21:01:58 -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 <3ATEmZAcKCiQKOMLODIGOOGLE.COMQEMU-DEVELNONGNU.ORG@flex--komlodi.bounces.google.com>) id 1pi39Q-0003FY-Sm for qemu-devel@nongnu.org; Thu, 30 Mar 2023 21:01:56 -0400 Received: from mail-pf1-x449.google.com ([2607:f8b0:4864:20::449]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from <3ATEmZAcKCiQKOMLODIGOOGLE.COMQEMU-DEVELNONGNU.ORG@flex--komlodi.bounces.google.com>) id 1pi39P-0006cB-50 for qemu-devel@nongnu.org; Thu, 30 Mar 2023 21:01:56 -0400 Received: by mail-pf1-x449.google.com with SMTP id y15-20020a62f24f000000b00627dd180a30so9525328pfl.6 for ; Thu, 30 Mar 2023 18:01:54 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20210112; t=1680224513; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=xxmkfnav+C8zEtIsn1y237i6/yeHQ5Aob1crdW87PWU=; b=b4G2rzgA5Erkrvl/gkIl6GWrBcrb9+RGJhdAZVn3WWMai15SN85W3i8e6Ddh21BkUc b3ifrpC5nayi0YtRsdHBadpGsSaB08ZV6heZAggxIYORoe2FjL5k5Geq7WGUw+vhsXLK EglxpBtFvBIvONfu8IW6S5Ru0SWZD/rzIs9jJ2YZwvWxSUlPMkZ+v3QbAsR7AdT0PPGx tcaSaa+gR0qqla9S5fuqsDDgNlHHv/vHOkDfnTiC9WV6BpnNEYciCHwk2sVsQKCcLdxg yJSLRCu028muQuVeFN1MyIk1gXMgOWUTjl7IKBLcCl3Lzy5WtL3EWLfTiCrHI2WirNRs sf6A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; t=1680224513; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=xxmkfnav+C8zEtIsn1y237i6/yeHQ5Aob1crdW87PWU=; b=wl6ERerhAqa+4rkJ5TxEVqFggImG36rAlDP8G0PALwq9yGIXWnhZowoKmLtqXYkgWO WJ+SFixUc5WnKPJry+HBUT14+EnQfwCcBGDyB95AO3fFKYBazVXql+kvkmevsS1HwvPU ZXNx7ANSHPa1bTlMKIUmWV2qx5zF9Gl3LnhOmVBv/DJOgr7trBeCNwb3lUgGg79M0oGL dJAucbXrNFvrpQVC1aCpo1FoB5uGRrW1qvb/CwD3iW1VnLIuNLYOEIvD2stiUHew/INv 6sHm0cryOmvsP9kGze7W8JDWzqq4jUOrMK1q8hc/WVXdeq+t/HXVfzqRhipSM4OI2KlQ h3nQ== X-Gm-Message-State: AAQBX9c0UJ86Na3UX9/Nva4U0h5XkFRTNwObELLp0KWIhE6K/u64e7LP qA64ACn9GOJi3PiKOWeCzCDMHbK3pGJncF3XNsgdUUcAgtupVfl9Jq4zKxy4veB/gX9TAQrklXw dLQgG1i7dWV5VW0TCT7taMS340z5it2zzE+o8te6lMOWQ5vIxDPCSwelUPaILshU= X-Google-Smtp-Source: AKy350Zbgjru9zzZPZQ6RSAF+4Pu+S6WgUAAV1gWezGRr0qbIAFqWApQuu1oVFvcNQjOXYyj+HdE32Qeqi1k X-Received: from komlodi.c.googlers.com ([fda3:e722:ac3:cc00:7f:e700:c0a8:35ee]) (user=komlodi job=sendgmr) by 2002:a17:90a:c095:b0:234:acfd:c8da with SMTP id o21-20020a17090ac09500b00234acfdc8damr7919148pjs.2.1680224513354; Thu, 30 Mar 2023 18:01:53 -0700 (PDT) Date: Fri, 31 Mar 2023 01:01:27 +0000 In-Reply-To: <20230331010131.1412571-1-komlodi@google.com> Mime-Version: 1.0 References: <20230331010131.1412571-1-komlodi@google.com> X-Mailer: git-send-email 2.40.0.348.gf938b09366-goog Message-ID: <20230331010131.1412571-13-komlodi@google.com> Subject: [PATCH 12/16] hw/i3c/aspeed_i3c: Add controller resets From: Joe Komlodi To: qemu-devel@nongnu.org Cc: venture@google.com, komlodi@google.com, peter.maydell@linaro.org Received-SPF: pass client-ip=2607:f8b0:4864:20::449; envelope-from=3ATEmZAcKCiQKOMLODIGOOGLE.COMQEMU-DEVELNONGNU.ORG@flex--komlodi.bounces.google.com; helo=mail-pf1-x449.google.com X-Spam_score_int: -95 X-Spam_score: -9.6 X-Spam_bar: --------- X-Spam_report: (-9.6 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_MED=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, USER_IN_DEF_DKIM_WL=-7.5 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+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Adds behavior to the device reset register. Signed-off-by: Joe Komlodi Reviewed-by: Patrick Venture Reviewed-by: Stephen Longfield --- hw/i3c/aspeed_i3c.c | 110 +++++++++++++++++++++++++++++++++++++++++--- hw/i3c/trace-events | 1 + 2 files changed, 104 insertions(+), 7 deletions(-) diff --git a/hw/i3c/aspeed_i3c.c b/hw/i3c/aspeed_i3c.c index 4c0c074012..19d766f074 100644 --- a/hw/i3c/aspeed_i3c.c +++ b/hw/i3c/aspeed_i3c.c @@ -938,6 +938,108 @@ static void aspeed_i3c_device_intr_force_w(AspeedI3CDevice *s, uint32_t val) aspeed_i3c_device_update_irq(s); } +static void aspeed_i3c_device_cmd_queue_reset(AspeedI3CDevice *s) +{ + fifo32_reset(&s->cmd_queue); + + ARRAY_FIELD_DP32(s->regs, QUEUE_STATUS_LEVEL, CMD_QUEUE_EMPTY_LOC, + fifo32_num_free(&s->cmd_queue)); + uint8_t empty_threshold = ARRAY_FIELD_EX32(s->regs, QUEUE_THLD_CTRL, + CMD_BUF_EMPTY_THLD); + if (fifo32_num_free(&s->cmd_queue) >= empty_threshold) { + ARRAY_FIELD_DP32(s->regs, INTR_STATUS, CMD_QUEUE_RDY, 1); + aspeed_i3c_device_update_irq(s); + }; +} + +static void aspeed_i3c_device_resp_queue_reset(AspeedI3CDevice *s) +{ + fifo32_reset(&s->resp_queue); + + ARRAY_FIELD_DP32(s->regs, QUEUE_STATUS_LEVEL, RESP_BUF_BLR, + fifo32_num_used(&s->resp_queue)); + /* + * This interrupt will always be cleared because the threshold is a minimum + * of 1 and the queue size is 0. + */ + ARRAY_FIELD_DP32(s->regs, INTR_STATUS, RESP_RDY, 0); + aspeed_i3c_device_update_irq(s); +} + +static void aspeed_i3c_device_ibi_queue_reset(AspeedI3CDevice *s) +{ + fifo32_reset(&s->ibi_queue); + + ARRAY_FIELD_DP32(s->regs, QUEUE_STATUS_LEVEL, IBI_BUF_BLR, + fifo32_num_used(&s->resp_queue)); + /* + * This interrupt will always be cleared because the threshold is a minimum + * of 1 and the queue size is 0. + */ + ARRAY_FIELD_DP32(s->regs, INTR_STATUS, IBI_THLD, 0); + aspeed_i3c_device_update_irq(s); +} + +static void aspeed_i3c_device_tx_queue_reset(AspeedI3CDevice *s) +{ + fifo32_reset(&s->tx_queue); + + ARRAY_FIELD_DP32(s->regs, DATA_BUFFER_STATUS_LEVEL, TX_BUF_EMPTY_LOC, + fifo32_num_free(&s->tx_queue)); + /* TX buf is empty, so this interrupt will always be set. */ + ARRAY_FIELD_DP32(s->regs, INTR_STATUS, TX_THLD, 1); + aspeed_i3c_device_update_irq(s); +} + +static void aspeed_i3c_device_rx_queue_reset(AspeedI3CDevice *s) +{ + fifo32_reset(&s->rx_queue); + + ARRAY_FIELD_DP32(s->regs, DATA_BUFFER_STATUS_LEVEL, RX_BUF_BLR, + fifo32_num_used(&s->resp_queue)); + /* + * This interrupt will always be cleared because the threshold is a minimum + * of 1 and the queue size is 0. + */ + ARRAY_FIELD_DP32(s->regs, INTR_STATUS, RX_THLD, 0); + aspeed_i3c_device_update_irq(s); +} + +static void aspeed_i3c_device_reset(DeviceState *dev) +{ + AspeedI3CDevice *s = ASPEED_I3C_DEVICE(dev); + trace_aspeed_i3c_device_reset(s->id); + + memcpy(s->regs, ast2600_i3c_device_resets, sizeof(s->regs)); + aspeed_i3c_device_cmd_queue_reset(s); + aspeed_i3c_device_resp_queue_reset(s); + aspeed_i3c_device_ibi_queue_reset(s); + aspeed_i3c_device_tx_queue_reset(s); + aspeed_i3c_device_rx_queue_reset(s); +} + +static void aspeed_i3c_device_reset_ctrl_w(AspeedI3CDevice *s, uint32_t val) +{ + if (FIELD_EX32(val, RESET_CTRL, CORE_RESET)) { + aspeed_i3c_device_reset(DEVICE(s)); + } + if (FIELD_EX32(val, RESET_CTRL, CMD_QUEUE_RESET)) { + aspeed_i3c_device_cmd_queue_reset(s); + } + if (FIELD_EX32(val, RESET_CTRL, RESP_QUEUE_RESET)) { + aspeed_i3c_device_resp_queue_reset(s); + } + if (FIELD_EX32(val, RESET_CTRL, TX_BUF_RESET)) { + aspeed_i3c_device_tx_queue_reset(s); + } + if (FIELD_EX32(val, RESET_CTRL, RX_BUF_RESET)) { + aspeed_i3c_device_rx_queue_reset(s); + } + if (FIELD_EX32(val, RESET_CTRL, IBI_QUEUE_RESET)) { + aspeed_i3c_device_ibi_queue_reset(s); + } +} + static uint32_t aspeed_i3c_device_pop_rx(AspeedI3CDevice *s) { if (fifo32_is_empty(&s->rx_queue)) { @@ -1693,6 +1795,7 @@ static void aspeed_i3c_device_write(void *opaque, hwaddr offset, aspeed_i3c_device_cmd_queue_port_w(s, val32); break; case R_RESET_CTRL: + aspeed_i3c_device_reset_ctrl_w(s, val32); break; case R_INTR_STATUS: aspeed_i3c_device_intr_status_w(s, val32); @@ -1728,13 +1831,6 @@ static const MemoryRegionOps aspeed_i3c_device_ops = { .endianness = DEVICE_LITTLE_ENDIAN, }; -static void aspeed_i3c_device_reset(DeviceState *dev) -{ - AspeedI3CDevice *s = ASPEED_I3C_DEVICE(dev); - - memcpy(s->regs, ast2600_i3c_device_resets, sizeof(s->regs)); -} - static void aspeed_i3c_device_realize(DeviceState *dev, Error **errp) { AspeedI3CDevice *s = ASPEED_I3C_DEVICE(dev); diff --git a/hw/i3c/trace-events b/hw/i3c/trace-events index 7a202b28c9..71e3059f8d 100644 --- a/hw/i3c/trace-events +++ b/hw/i3c/trace-events @@ -9,6 +9,7 @@ aspeed_i3c_device_send(uint32_t deviceid, uint32_t num_bytes) "I3C Dev[%u] send aspeed_i3c_device_recv_data(uint32_t deviceid, uint32_t num_bytes) "I3C Dev[%u] recv %" PRId32 " bytes from bus" aspeed_i3c_device_ibi_recv(uint32_t deviceid, uint8_t ibi_byte) "I3C Dev[%u] recv IBI byte 0x%" PRIx8 aspeed_i3c_device_ibi_handle(uint32_t deviceid, uint8_t addr, bool rnw) "I3C Dev[%u] handle IBI from address 0x%" PRIx8 " RnW=%d" +aspeed_i3c_device_reset(uint32_t deviceid) "I3C Dev[%u] reset" aspeed_i3c_device_pop_rx(uint32_t deviceid, uint32_t data) "I3C Dev[%u] pop 0x%" PRIx32 " from RX FIFO" aspeed_i3c_device_resp_queue_push(uint32_t deviceid, uint32_t data) "I3C Dev[%u] push 0x%" PRIx32 " to response queue" aspeed_i3c_device_push_tx(uint32_t deviceid, uint32_t data) "I3C Dev[%u] push 0x%" PRIx32 " to TX FIFO" From patchwork Fri Mar 31 01:01:28 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Joe Komlodi X-Patchwork-Id: 1763558 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=nongnu.org (client-ip=209.51.188.17; helo=lists.gnu.org; envelope-from=qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org; receiver=) Authentication-Results: legolas.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=google.com header.i=@google.com header.a=rsa-sha256 header.s=20210112 header.b=N5LcqOzW; dkim-atps=neutral Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-ECDSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4Pnhsj2Js1z1yYr for ; Fri, 31 Mar 2023 12:05:13 +1100 (AEDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1pi39U-0003Gj-JF; Thu, 30 Mar 2023 21:02:00 -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 <3AzEmZAcKCiYMQONQFKIQQING.EQOSGOW-FGXGNPQPIPW.QTI@flex--komlodi.bounces.google.com>) id 1pi39S-0003GF-OM for qemu-devel@nongnu.org; Thu, 30 Mar 2023 21:01:58 -0400 Received: from mail-yb1-xb4a.google.com ([2607:f8b0:4864:20::b4a]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from <3AzEmZAcKCiYMQONQFKIQQING.EQOSGOW-FGXGNPQPIPW.QTI@flex--komlodi.bounces.google.com>) id 1pi39Q-0006cW-Hb for qemu-devel@nongnu.org; Thu, 30 Mar 2023 21:01:58 -0400 Received: by mail-yb1-xb4a.google.com with SMTP id e186-20020a2537c3000000b00b72501acf50so20660617yba.20 for ; Thu, 30 Mar 2023 18:01:55 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20210112; t=1680224515; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=o2ENwSuVAXUNhtrBnxjBNDFE8Q8MI8xhRWexpQ+m/VQ=; b=N5LcqOzWJp6EynDhdv+u/sdMJ6BJZvIOauwPeElfFxTnQRAxflbOovdUDYOI75r2l2 7cLzlAmvqguUbhU9JZfT2pqXdRtXpi88Glpj3Z5yPXkJSkGRwugL+dlYUz4L427Jwy9s /UwtGzKHiwxjNUe23+38/Re9lMP8eKFV/xFmcp+9aUspu8YKboJTBPArbSGALDyU6+0r Zcd60GSVLF4gcqJp76gJGhRtdmFqfyngrLT9SQxTSVD5IKqgzaQ/tDdepPwqrD577mvJ gzMdjxUkADpk9Kk3Lq6Ga28k3jbhLioycUt3QtpCNh41Z2xwl+Eoxwgv6hbL7qN1iHD8 4mvg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; t=1680224515; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=o2ENwSuVAXUNhtrBnxjBNDFE8Q8MI8xhRWexpQ+m/VQ=; b=auIg8NBpsM6uDxhj41p4bug5gHR3zASH3s4JFRXEtWJwpC3Lnj2KudFIVElMAIhQ9x 0Qa+5Ld5mwObrA3AJ1yBh/+S85Ql0f2Udl/5j8oC/6jiFUcbG1YPp32Huv3VkAAXr8Iu ROPnU98UOjCnHrpQ37s/5vN7oYXkW0QcyP/qAfABML98N43EcZ9trUx9Mqj2lk6rB5v7 UMEAG2MpVwE+vIG+DewakaHGoGkq+KshAWLLMp+gIz1t+z3i681XXhUodp8pFWYAv5wO V5VwhovPW+37eBu+3lHP1ZnZ7A31bSdmxndOcbL4ZQatiuNMxVOMRr/bkw6CIYdsj8Bg oyaw== X-Gm-Message-State: AAQBX9eBEQQudTr/hLEAcaOOwBd5poiQFWloqIlYlmZfdLE2HTYSOtuD Cgm31PBwmgmsETdchhWo4FJ/yugMPv7z+Dgy/EwXwe8xH21MDcAWFDDLIhEZGzOQ6uuFAllFUfx 57Hsk3hJeFbvy+v8xeuL31ROHvDcAd819+4YnUXd1IbVfnGbUjYEn6b7+yNoyMyQ= X-Google-Smtp-Source: AKy350bdsrlNSZS4Xoln6FeN51C/WR5SV4oVGQOLfeyBJ1jBN3HN/g29TxyIZJXLXi8mkmvscgcLLSjkptus X-Received: from komlodi.c.googlers.com ([fda3:e722:ac3:cc00:7f:e700:c0a8:35ee]) (user=komlodi job=sendgmr) by 2002:a05:6902:1004:b0:b75:968e:f282 with SMTP id w4-20020a056902100400b00b75968ef282mr16619608ybt.11.1680224515023; Thu, 30 Mar 2023 18:01:55 -0700 (PDT) Date: Fri, 31 Mar 2023 01:01:28 +0000 In-Reply-To: <20230331010131.1412571-1-komlodi@google.com> Mime-Version: 1.0 References: <20230331010131.1412571-1-komlodi@google.com> X-Mailer: git-send-email 2.40.0.348.gf938b09366-goog Message-ID: <20230331010131.1412571-14-komlodi@google.com> Subject: [PATCH 13/16] hw/i3c: Add Mock target From: Joe Komlodi To: qemu-devel@nongnu.org Cc: venture@google.com, komlodi@google.com, peter.maydell@linaro.org Received-SPF: pass client-ip=2607:f8b0:4864:20::b4a; envelope-from=3AzEmZAcKCiYMQONQFKIQQING.EQOSGOW-FGXGNPQPIPW.QTI@flex--komlodi.bounces.google.com; helo=mail-yb1-xb4a.google.com X-Spam_score_int: -95 X-Spam_score: -9.6 X-Spam_bar: --------- X-Spam_report: (-9.6 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_MED=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, USER_IN_DEF_DKIM_WL=-7.5 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+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Adds a simple i3c device to be used for testing in lieu of a real device. The mock target supports the following features: - A buffer that users can read and write to. - CCC support for commonly used CCCs when probing devices on an I3C bus. - IBI sending upon receiving a user-defined byte. Signed-off-by: Joe Komlodi Reviewed-by: Titus Rwantare Reviewed-by: Patrick Venture --- hw/i3c/Kconfig | 10 ++ hw/i3c/meson.build | 1 + hw/i3c/mock-target.c | 312 +++++++++++++++++++++++++++++++++++ hw/i3c/trace-events | 9 + include/hw/i3c/mock-target.h | 60 +++++++ 5 files changed, 392 insertions(+) create mode 100644 hw/i3c/mock-target.c create mode 100644 include/hw/i3c/mock-target.h diff --git a/hw/i3c/Kconfig b/hw/i3c/Kconfig index e07fe445c6..f97f4066a9 100644 --- a/hw/i3c/Kconfig +++ b/hw/i3c/Kconfig @@ -1,2 +1,12 @@ config I3C bool + +config I3C_DEVICES + # Device group for i3c devices which can reasonably be user-plugged to any + # board's i3c bus. + bool + +config MOCK_TARGET + bool + select I3C + default y if I3C_DEVICES diff --git a/hw/i3c/meson.build b/hw/i3c/meson.build index 3599f1a396..0f6268063e 100644 --- a/hw/i3c/meson.build +++ b/hw/i3c/meson.build @@ -1,4 +1,5 @@ i3c_ss = ss.source_set() i3c_ss.add(when: 'CONFIG_I3C', if_true: files('core.c')) i3c_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('aspeed_i3c.c')) +i3c_ss.add(when: 'CONFIG_MOCK_TARGET', if_true: files('mock-target.c')) softmmu_ss.add_all(when: 'CONFIG_I3C', if_true: i3c_ss) diff --git a/hw/i3c/mock-target.c b/hw/i3c/mock-target.c new file mode 100644 index 0000000000..388d21d5ca --- /dev/null +++ b/hw/i3c/mock-target.c @@ -0,0 +1,312 @@ +/* + * Mock I3C Device + * + * Copyright (c) 2023 Google LLC + * + * The mock I3C device can be thought of as a simple EEPROM. It has a buffer, + * and the pointer in the buffer is reset to 0 on an I3C STOP. + * To write to the buffer, issue a private write and send data. + * To read from the buffer, issue a private read. + * + * The mock target also supports sending target interrupt IBIs. + * To issue an IBI, set the 'ibi-magic-num' property to a non-zero number, and + * send that number in a private transaction. The mock target will issue an IBI + * after 1 second. + * + * It also supports a handful of CCCs that are typically used when probing I3C + * devices. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 General Public License + * for more details. + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "trace.h" +#include "hw/i3c/i3c.h" +#include "hw/i3c/mock-target.h" +#include "hw/irq.h" +#include "hw/qdev-properties.h" +#include "qapi/error.h" +#include "qemu/module.h" + +#ifndef MOCK_TARGET_DEBUG +#define MOCK_TARGET_DEBUG 0 +#endif + +#define DB_PRINTF(...) do { \ + if (MOCK_TARGET_DEBUG) { \ + qemu_log("%s: ", __func__); \ + qemu_log(__VA_ARGS__); \ + } \ + } while (0) + +#define IBI_DELAY_NS (1 * 1000 * 1000) + +static uint32_t mock_target_rx(I3CTarget *i3c, uint8_t *data, + uint32_t num_to_read) +{ + MockTargetState *s = MOCK_TARGET(i3c); + uint32_t i; + + /* Bounds check. */ + if (s->p_buf == s->cfg.buf_size) { + return 0; + } + + for (i = 0; i < num_to_read; i++) { + data[i] = s->buf[s->p_buf]; + trace_mock_target_rx(data[i]); + s->p_buf++; + if (s->p_buf == s->cfg.buf_size) { + break; + } + } + + /* Return the number of bytes we're sending to the controller. */ + return i; +} + +static void mock_target_ibi_timer_start(MockTargetState *s) +{ + int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + timer_mod(&s->qtimer, now + IBI_DELAY_NS); +} + +static int mock_target_tx(I3CTarget *i3c, const uint8_t *data, + uint32_t num_to_send, uint32_t *num_sent) +{ + MockTargetState *s = MOCK_TARGET(i3c); + int ret; + uint32_t to_write; + + if (s->cfg.ibi_magic && num_to_send == 1 && s->cfg.ibi_magic == *data) { + mock_target_ibi_timer_start(s); + return 0; + } + + /* Bounds check. */ + if (num_to_send + s->p_buf > s->cfg.buf_size) { + to_write = s->cfg.buf_size - s->p_buf; + ret = -1; + } else { + to_write = num_to_send; + ret = 0; + } + for (uint32_t i = 0; i < to_write; i++) { + trace_mock_target_tx(data[i]); + s->buf[s->p_buf] = data[i]; + s->p_buf++; + } + return ret; +} + +static int mock_target_event(I3CTarget *i3c, enum I3CEvent event) +{ + MockTargetState *s = MOCK_TARGET(i3c); + + trace_mock_target_event(event); + if (event == I3C_STOP) { + s->in_ccc = false; + s->curr_ccc = 0; + s->ccc_byte_offset = 0; + s->p_buf = 0; + } + + return 0; +} + +static int mock_target_handle_ccc_read(I3CTarget *i3c, uint8_t *data, + uint32_t num_to_read, uint32_t *num_read) +{ + MockTargetState *s = MOCK_TARGET(i3c); + + switch (s->curr_ccc) { + case I3C_CCCD_GETMXDS: + /* Default data rate for I3C. */ + while (s->ccc_byte_offset < num_to_read) { + if (s->ccc_byte_offset >= 2) { + break; + } + data[s->ccc_byte_offset] = 0; + *num_read = s->ccc_byte_offset; + s->ccc_byte_offset++; + } + break; + case I3C_CCCD_GETCAPS: + /* Support I3C version 1.1.x, no other features. */ + while (s->ccc_byte_offset < num_to_read) { + if (s->ccc_byte_offset >= 2) { + break; + } + if (s->ccc_byte_offset == 0) { + data[s->ccc_byte_offset] = 0; + } else { + data[s->ccc_byte_offset] = 0x01; + } + *num_read = s->ccc_byte_offset; + s->ccc_byte_offset++; + } + break; + case I3C_CCCD_GETMWL: + case I3C_CCCD_GETMRL: + /* MWL/MRL is MSB first. */ + while (s->ccc_byte_offset < num_to_read) { + if (s->ccc_byte_offset >= 2) { + break; + } + data[s->ccc_byte_offset] = (s->cfg.buf_size & + (0xff00 >> (s->ccc_byte_offset * 8))) >> + (8 - (s->ccc_byte_offset * 8)); + s->ccc_byte_offset++; + *num_read = num_to_read; + } + break; + case I3C_CCC_ENTDAA: + case I3C_CCCD_GETPID: + case I3C_CCCD_GETBCR: + case I3C_CCCD_GETDCR: + /* Nothing to do. */ + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "Unhandled CCC 0x%.2x\n", s->curr_ccc); + return -1; + } + + trace_mock_target_handle_ccc_read(*num_read, num_to_read); + return 0; +} + +static int mock_target_handle_ccc_write(I3CTarget *i3c, const uint8_t *data, + uint32_t num_to_send, + uint32_t *num_sent) +{ + MockTargetState *s = MOCK_TARGET(i3c); + + if (!s->curr_ccc) { + s->in_ccc = true; + s->curr_ccc = *data; + trace_mock_target_new_ccc(s->curr_ccc); + } + + *num_sent = 1; + switch (s->curr_ccc) { + case I3C_CCC_ENEC: + s->can_ibi = true; + break; + case I3C_CCC_DISEC: + s->can_ibi = false; + break; + case I3C_CCC_ENTDAA: + case I3C_CCC_SETAASA: + case I3C_CCC_RSTDAA: + case I3C_CCCD_SETDASA: + case I3C_CCCD_GETPID: + case I3C_CCCD_GETBCR: + case I3C_CCCD_GETDCR: + case I3C_CCCD_GETMWL: + case I3C_CCCD_GETMRL: + case I3C_CCCD_GETMXDS: + case I3C_CCCD_GETCAPS: + /* Nothing to do. */ + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "Unhandled CCC 0x%.2x\n", s->curr_ccc); + return -1; + } + + trace_mock_target_handle_ccc_read(*num_sent, num_to_send); + return 0; +} + +static void mock_target_do_ibi(MockTargetState *s) +{ + if (!s->can_ibi) { + DB_PRINTF("IBIs disabled by controller"); + return; + } + + trace_mock_target_do_ibi(s->i3c.address, true); + int nack = i3c_target_send_ibi(&s->i3c, s->i3c.address, /*is_recv=*/true); + /* Getting NACKed isn't necessarily an error, just print it out. */ + if (nack) { + DB_PRINTF("NACKed from controller when sending target interrupt.\n"); + } +} + +static void mock_target_timer_elapsed(void *opaque) +{ + MockTargetState *s = MOCK_TARGET(opaque); + timer_del(&s->qtimer); + mock_target_do_ibi(s); +} + +static void mock_target_reset(I3CTarget *i3c) +{ + MockTargetState *s = MOCK_TARGET(i3c); + s->can_ibi = false; +} + +static void mock_target_realize(DeviceState *dev, Error **errp) +{ + MockTargetState *s = MOCK_TARGET(dev); + s->buf = g_new0(uint8_t, s->cfg.buf_size); + mock_target_reset(&s->i3c); +} + +static void mock_target_init(Object *obj) +{ + MockTargetState *s = MOCK_TARGET(obj); + s->can_ibi = false; + + /* For IBIs. */ + timer_init_ns(&s->qtimer, QEMU_CLOCK_VIRTUAL, mock_target_timer_elapsed, s); +} + +static Property remote_i3c_props[] = { + /* The size of the internal buffer. */ + DEFINE_PROP_UINT32("buf-size", MockTargetState, cfg.buf_size, 0x100), + /* + * If the mock target receives this number, it will issue an IBI after + * 1 second. Disabled if the IBI magic number is 0. + */ + DEFINE_PROP_UINT8("ibi-magic-num", MockTargetState, cfg.ibi_magic, 0x00), + DEFINE_PROP_END_OF_LIST(), +}; + +static void mock_target_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + I3CTargetClass *k = I3C_TARGET_CLASS(klass); + + dc->realize = mock_target_realize; + k->event = mock_target_event; + k->recv = mock_target_rx; + k->send = mock_target_tx; + k->handle_ccc_read = mock_target_handle_ccc_read; + k->handle_ccc_write = mock_target_handle_ccc_write; + + device_class_set_props(dc, remote_i3c_props); +} + +static const TypeInfo mock_target_info = { + .name = TYPE_MOCK_TARGET, + .parent = TYPE_I3C_TARGET, + .instance_size = sizeof(MockTargetState), + .instance_init = mock_target_init, + .class_init = mock_target_class_init, +}; + +static void mock_target_register_types(void) +{ + type_register_static(&mock_target_info); +} + +type_init(mock_target_register_types) diff --git a/hw/i3c/trace-events b/hw/i3c/trace-events index 71e3059f8d..74c5a5dd6e 100644 --- a/hw/i3c/trace-events +++ b/hw/i3c/trace-events @@ -34,3 +34,12 @@ legacy_i2c_recv(uint8_t byte) "Legacy I2C recv 0x%" PRIx8 legacy_i2c_send(uint8_t byte) "Legacy I2C send 0x%" PRIx8 legacy_i2c_start_transfer(uint8_t address, bool is_recv) "Legacy I2C START with address 0x%" PRIx8 " is_recv=%d" legacy_i2c_end_transfer(void) "Legacy I2C STOP" + +# mock-target.c +mock_target_rx(uint8_t byte) "I3C mock target read 0x%" PRIx8 +mock_target_tx(uint8_t byte) "I3C mock target write 0x%" PRIx8 +mock_target_event(uint8_t event) "I3C mock target event 0x%" PRIx8 +mock_target_handle_ccc_read(uint32_t num_read, uint32_t num_to_read) "I3C mock target read %" PRId32 "/%" PRId32 " bytes" +mock_target_new_ccc(uint8_t ccc) "I3C mock target handle CCC 0x%" PRIx8 +mock_target_handle_ccc_write(uint32_t num_sent, uint32_t num_to_send) "I3C mock target send %" PRId32 "/%" PRId32 " bytes" +mock_target_do_ibi(uint8_t address, bool is_recv) "I3C mock target IBI with address 0x%" PRIx8 " RnW=%d" diff --git a/include/hw/i3c/mock-target.h b/include/hw/i3c/mock-target.h new file mode 100644 index 0000000000..87df0e014b --- /dev/null +++ b/include/hw/i3c/mock-target.h @@ -0,0 +1,60 @@ +#ifndef MOCK_TARGET_H_ +#define MOCK_TARGET_H_ + +/* + * Mock I3C Device + * + * Copyright (c) 2023 Google LLC + * + * The mock I3C device can be thought of as a simple EEPROM. It has a buffer, + * and the pointer in the buffer is reset to 0 on an I3C STOP. + * To write to the buffer, issue a private write and send data. + * To read from the buffer, issue a private read. + * + * The mock target also supports sending target interrupt IBIs. + * To issue an IBI, set the 'ibi-magic-num' property to a non-zero number, and + * send that number in a private transaction. The mock target will issue an IBI + * after 1 second. + * + * It also supports a handful of CCCs that are typically used when probing I3C + * devices. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 General Public License + * for more details. + */ + +#include "qemu/osdep.h" +#include "qemu/timer.h" +#include "hw/i3c/i3c.h" + +#define TYPE_MOCK_TARGET "mock-target" +OBJECT_DECLARE_SIMPLE_TYPE(MockTargetState, MOCK_TARGET) + +struct MockTargetState { + I3CTarget i3c; + + /* General device state */ + bool can_ibi; + QEMUTimer qtimer; + size_t p_buf; + uint8_t *buf; + + /* For Handing CCCs. */ + bool in_ccc; + I3CCCC curr_ccc; + uint8_t ccc_byte_offset; + + struct { + uint32_t buf_size; + uint8_t ibi_magic; + } cfg; +}; + +#endif From patchwork Fri Mar 31 01:01:29 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Joe Komlodi X-Patchwork-Id: 1763560 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=nongnu.org (client-ip=209.51.188.17; helo=lists.gnu.org; envelope-from=qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org; receiver=) Authentication-Results: legolas.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=google.com header.i=@google.com header.a=rsa-sha256 header.s=20210112 header.b=kI4haCV0; dkim-atps=neutral Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-ECDSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4Pnhsk0q9nz1yY8 for ; Fri, 31 Mar 2023 12:05:14 +1100 (AEDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1pi39W-0003IZ-64; Thu, 30 Mar 2023 21:02:02 -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 <3BDEmZAcKCicNRPORGLJRRJOH.FRPTHPX-GHYHOQRQJQX.RUJ@flex--komlodi.bounces.google.com>) id 1pi39V-0003Gk-0I for qemu-devel@nongnu.org; Thu, 30 Mar 2023 21:02:01 -0400 Received: from mail-pl1-x649.google.com ([2607:f8b0:4864:20::649]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from <3BDEmZAcKCicNRPORGLJRRJOH.FRPTHPX-GHYHOQRQJQX.RUJ@flex--komlodi.bounces.google.com>) id 1pi39S-0006ch-8s for qemu-devel@nongnu.org; Thu, 30 Mar 2023 21:02:00 -0400 Received: by mail-pl1-x649.google.com with SMTP id kp6-20020a170903280600b001a2945a7fdeso1175561plb.18 for ; Thu, 30 Mar 2023 18:01:57 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20210112; t=1680224516; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=8JwBrM+TQ4UJZK9/xq307nM32tJrEu/DqbElWjwaj4E=; b=kI4haCV0UE5dEZGmlgFJnlNrdKk9UQxtoZxnKjisK7cPyZWUGskvlr0gEZweD5H0pW JoOOznxAN2sv6UpcyBCW7lfusIE12NkSgMiCjU1Q+7rfcvja3Bu070lytwqUjez2GWSA PuOfuBoFH5g7wc+KeMC40pahOV0eETa/8Z27Zoy+PldfqmjFEBD+j/xGbGJim1tJnmBc dKFSMVoMarHIkleqI40cHCYQ1RKO5PzbnKFf91/b31GaB/o9g06TvIlC+ekrUdCZxdc+ uLAvmCLbYa0BKiz7vL2oNMnePL7A5989VaJ7r0/v8pAZrrc+VMCbf0B/PLVseyKxH4Ci ANsg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; t=1680224516; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=8JwBrM+TQ4UJZK9/xq307nM32tJrEu/DqbElWjwaj4E=; b=y+Sa6sM02YhCaRdFeP0SW7kidRQH/bOcqZS18SVeeUtd2jmn0eTLh7GskqZW9+xSub lxiArfGqSAoc4DeizHP9d7jgusBSrizUOOmK+2mnNXvJozo7g0M1nirZ8t0E0qtlbBlr X8XRu63nvzTPVFHYVZqYpk7TYTDv/3+1N2CHx+FixkMp19AarGJbiD/5fjI2MY8Q08ey wQX1AoDeZb8kNrm1wKQdYi3eK1uBQRmC6UYEdHNvFo460ui6Nj+Lo1uWK2zzIi6c706w 8WrBk2Nb83qsvxr6/esC54e7T+DBCN5Q8AH5YRKX5FvCYDApOan9j0JZNNrErsXvSYI1 Nm1g== X-Gm-Message-State: AAQBX9eWaHeM9YVPug2gtn8rs9jA+dAO36G2WN6IaUQ96RlLHeIYzT3W 7rVR6FWs0WG/NqqTwUS2wcbmyjeltX6pGX6p7pWW5z3HyMEAwIwUJDBXWQBkzf3N/6n0Kxl0drP ElNOYxyYodyBAeIc1PBzMX2YAvdgpLXgzaK1XJcuJeQOWGm6g8T0s0plsb/odnCQ= X-Google-Smtp-Source: AKy350bQxS7ySuj8jT8fr/jhdVGyEca0kBEcwWa8RVBS2SfMfhJjKthrHXITVB9qTwOAR7WfWa77KPEPsLxo X-Received: from komlodi.c.googlers.com ([fda3:e722:ac3:cc00:7f:e700:c0a8:35ee]) (user=komlodi job=sendgmr) by 2002:a05:6a00:1883:b0:625:cff9:8b8d with SMTP id x3-20020a056a00188300b00625cff98b8dmr12750020pfh.2.1680224516547; Thu, 30 Mar 2023 18:01:56 -0700 (PDT) Date: Fri, 31 Mar 2023 01:01:29 +0000 In-Reply-To: <20230331010131.1412571-1-komlodi@google.com> Mime-Version: 1.0 References: <20230331010131.1412571-1-komlodi@google.com> X-Mailer: git-send-email 2.40.0.348.gf938b09366-goog Message-ID: <20230331010131.1412571-15-komlodi@google.com> Subject: [PATCH 14/16] hw/i3c: remote_i3c: Add model From: Joe Komlodi To: qemu-devel@nongnu.org Cc: venture@google.com, komlodi@google.com, peter.maydell@linaro.org Received-SPF: pass client-ip=2607:f8b0:4864:20::649; envelope-from=3BDEmZAcKCicNRPORGLJRRJOH.FRPTHPX-GHYHOQRQJQX.RUJ@flex--komlodi.bounces.google.com; helo=mail-pl1-x649.google.com X-Spam_score_int: -95 X-Spam_score: -9.6 X-Spam_bar: --------- X-Spam_report: (-9.6 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_MED=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, USER_IN_DEF_DKIM_WL=-7.5 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+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Adds a model to communicate to remote I3C devices over chardev. This allows QEMU to communicate to I3C targets that exist outside of QEMU. The remote I3C protocol is as follows: On an I3C private and CCC TX (controller -> target) - 1-byte opcode - 4-byte number of bytes in the packet as a LE uint32 - n-byte payload On an I3C private and CCC RX (target -> controller) Controller to target: - 1-byte opcode - 4-byte number of bytes to read as a LE uint32 Remote target response: - 4-byte number of bytes in the packet as a LE uint32 - n-byte payload IBI (target -> controller, initiated by target) - 1-byte opcode - 1-byte IBI address - 1-byte RnW boolean - 4-byte length of IBI payload from target as a LE uint32 (can be 0) - n-byte IBI payload Signed-off-by: Joe Komlodi Reviewed-by: Patrick Venture Reviewed-by: Stephen Longfield --- hw/i3c/Kconfig | 5 + hw/i3c/meson.build | 1 + hw/i3c/remote-i3c.c | 469 ++++++++++++++++++++++++++++++++++++ hw/i3c/trace-events | 7 + include/hw/i3c/remote-i3c.h | 72 ++++++ 5 files changed, 554 insertions(+) create mode 100644 hw/i3c/remote-i3c.c create mode 100644 include/hw/i3c/remote-i3c.h diff --git a/hw/i3c/Kconfig b/hw/i3c/Kconfig index f97f4066a9..09b83578f3 100644 --- a/hw/i3c/Kconfig +++ b/hw/i3c/Kconfig @@ -10,3 +10,8 @@ config MOCK_TARGET bool select I3C default y if I3C_DEVICES + +config REMOTE_I3C + bool + select I3C + default y if I3C_DEVICES diff --git a/hw/i3c/meson.build b/hw/i3c/meson.build index 0f6268063e..5bc900de5b 100644 --- a/hw/i3c/meson.build +++ b/hw/i3c/meson.build @@ -2,4 +2,5 @@ i3c_ss = ss.source_set() i3c_ss.add(when: 'CONFIG_I3C', if_true: files('core.c')) i3c_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('aspeed_i3c.c')) i3c_ss.add(when: 'CONFIG_MOCK_TARGET', if_true: files('mock-target.c')) +i3c_ss.add(when: 'CONFIG_REMOTE_I3C', if_true: files('remote-i3c.c')) softmmu_ss.add_all(when: 'CONFIG_I3C', if_true: i3c_ss) diff --git a/hw/i3c/remote-i3c.c b/hw/i3c/remote-i3c.c new file mode 100644 index 0000000000..20a45e58d9 --- /dev/null +++ b/hw/i3c/remote-i3c.c @@ -0,0 +1,469 @@ +/* + * Remote I3C Device + * + * Copyright (c) 2023 Google LLC + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 General Public License + * for more details. + */ + +#include "qemu/osdep.h" +#include "qemu/bswap.h" +#include "qemu/log.h" +#include "qemu/fifo8.h" +#include "chardev/char-fe.h" +#include "trace.h" +#include "hw/i3c/i3c.h" +#include "hw/i3c/remote-i3c.h" +#include "hw/qdev-properties-system.h" + +typedef enum { + IBI_RX_STATE_DONE = 0, + IBI_RX_STATE_READ_ADDR = 1, + IBI_RX_STATE_READ_RNW = 2, + IBI_RX_STATE_READ_SIZE = 3, + IBI_RX_STATE_READ_DATA = 4, +} IBIRXState; + +typedef struct { + uint8_t addr; + bool is_recv; + uint32_t num_bytes; + uint8_t *data; +} IBIData; + +typedef struct { + I3CTarget parent_obj; + CharBackend chr; + /* For ease of debugging. */ + + struct { + char *name; + uint32_t buf_size; + } cfg; + + /* Intermediate buffer to store IBI data received over socket. */ + IBIData ibi_data; + Fifo8 tx_fifo; + Fifo8 rx_fifo; + uint8_t current_cmd; + IBIRXState ibi_rx_state; + /* + * To keep track of where we are in reading in data that's longer than + * 1-byte. + */ + uint32_t ibi_bytes_rxed; +} RemoteI3C; + +static uint32_t remote_i3c_recv(I3CTarget *t, uint8_t *data, + uint32_t num_to_read) +{ + RemoteI3C *i3c = REMOTE_I3C(t); + uint8_t type = REMOTE_I3C_RECV; + uint32_t num_read; + + qemu_chr_fe_write_all(&i3c->chr, &type, 1); + uint32_t num_to_read_le = cpu_to_le32(num_to_read); + qemu_chr_fe_write_all(&i3c->chr, (uint8_t *)&num_to_read_le, + sizeof(num_to_read_le)); + /* + * The response will first contain the size of the packet, as a LE uint32. + */ + qemu_chr_fe_read_all(&i3c->chr, (uint8_t *)&num_read, sizeof(num_read)); + + num_read = le32_to_cpu(num_read); + qemu_chr_fe_read_all(&i3c->chr, data, num_read); + trace_remote_i3c_recv(i3c->cfg.name, num_read, num_to_read); + return num_read; +} + +static inline bool remote_i3c_tx_in_progress(RemoteI3C *i3c) +{ + return !fifo8_is_empty(&i3c->tx_fifo); +} + +static int remote_i3c_chr_send_bytes(RemoteI3C *i3c) +{ + uint32_t i; + uint32_t num_bytes = fifo8_num_used(&i3c->tx_fifo); + g_autofree uint8_t *buf = g_new0(uint8_t, num_bytes); + + qemu_chr_fe_write_all(&i3c->chr, &i3c->current_cmd, + sizeof(i3c->current_cmd)); + + /* The FIFO data is stored in a ring buffer, move it into a linear one. */ + for (i = 0; i < num_bytes; i++) { + buf[i] = fifo8_pop(&i3c->tx_fifo); + } + + uint32_t num_bytes_le = cpu_to_le32(num_bytes); + qemu_chr_fe_write_all(&i3c->chr, (uint8_t *)&num_bytes_le, 4); + qemu_chr_fe_write_all(&i3c->chr, buf, num_bytes); + trace_remote_i3c_send(i3c->cfg.name, num_bytes, i3c->current_cmd == + REMOTE_I3C_HANDLE_CCC_WRITE); + + return 0; +} + +static bool remote_i3c_tx_fifo_push(RemoteI3C *i3c, const uint8_t *data, + uint32_t num_to_send, uint32_t *num_sent) +{ + uint32_t num_to_push = num_to_send; + bool ack = true; + + /* + * For performance reasons, we buffer data being sent from the controller to + * us. + * If this FIFO has data in it, we transmit it when we receive an I3C + * STOP or START. + */ + if (fifo8_num_free(&i3c->tx_fifo) < num_to_send) { + qemu_log_mask(LOG_GUEST_ERROR, "%s-%s: TX FIFO buffer full.\n", + object_get_canonical_path(OBJECT(i3c)), i3c->cfg.name); + num_to_push = fifo8_num_free(&i3c->tx_fifo); + ack = false; + } + + *num_sent = num_to_push; + for (uint32_t i = 0; i < num_to_push; i++) { + fifo8_push(&i3c->tx_fifo, data[i]); + } + + return ack; +} + +static int remote_i3c_send(I3CTarget *t, const uint8_t *data, + uint32_t num_to_send, uint32_t *num_sent) +{ + RemoteI3C *i3c = REMOTE_I3C(t); + i3c->current_cmd = REMOTE_I3C_SEND; + if (!remote_i3c_tx_fifo_push(i3c, data, num_to_send, num_sent)) { + return -1; + } + + return 0; +} + +static int remote_i3c_handle_ccc_read(I3CTarget *t, uint8_t *data, + uint32_t num_to_read, uint32_t *num_read) +{ + RemoteI3C *i3c = REMOTE_I3C(t); + uint8_t type = REMOTE_I3C_HANDLE_CCC_READ; + + qemu_chr_fe_write_all(&i3c->chr, &type, 1); + /* + * The response will first contain the size of the packet, as a LE uint32. + */ + qemu_chr_fe_read_all(&i3c->chr, (uint8_t *)num_read, 4); + *num_read = le32_to_cpu(*num_read); + qemu_chr_fe_read_all(&i3c->chr, data, *num_read); + trace_remote_i3c_ccc_read(i3c->cfg.name, *num_read, num_to_read); + + return 0; +} + +static int remote_i3c_handle_ccc_write(I3CTarget *t, const uint8_t *data, + uint32_t num_to_send, uint32_t *num_sent) +{ + RemoteI3C *i3c = REMOTE_I3C(t); + i3c->current_cmd = REMOTE_I3C_HANDLE_CCC_WRITE; + if (!remote_i3c_tx_fifo_push(i3c, data, num_to_send, num_sent)) { + return -1; + } + + return 0; +} + +static int remote_i3c_event(I3CTarget *t, enum I3CEvent event) +{ + RemoteI3C *i3c = REMOTE_I3C(t); + uint8_t type; + trace_remote_i3c_event(i3c->cfg.name, event); + switch (event) { + case I3C_START_RECV: + type = REMOTE_I3C_START_RECV; + break; + case I3C_START_SEND: + type = REMOTE_I3C_START_SEND; + break; + case I3C_STOP: + type = REMOTE_I3C_STOP; + break; + case I3C_NACK: + type = REMOTE_I3C_NACK; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s-%s: Unknown I3C event %d\n", + object_get_canonical_path(OBJECT(i3c)), i3c->cfg.name, + event); + return -1; + } + + /* + * If we have a transfer buffered, send it out before we tell the remote + * target about the next event. + */ + if (remote_i3c_tx_in_progress(i3c)) { + remote_i3c_chr_send_bytes(i3c); + } + + qemu_chr_fe_write_all(&i3c->chr, &type, 1); + return 0; +} + +static void remote_i3c_chr_event(void *opaque, QEMUChrEvent evt) +{ + switch (evt) { + case CHR_EVENT_OPENED: + case CHR_EVENT_CLOSED: + case CHR_EVENT_BREAK: + case CHR_EVENT_MUX_IN: + case CHR_EVENT_MUX_OUT: + /* + * Ignore events. + * Our behavior stays the same regardless of what happens. + */ + break; + default: + g_assert_not_reached(); + } +} + +static void remote_i3c_rx_ibi(RemoteI3C *i3c, const uint8_t *buf, int size) +{ + uint32_t p_buf = 0; + while (p_buf < size) { + switch (i3c->ibi_rx_state) { + /* This is the start of a new IBI request. */ + case IBI_RX_STATE_DONE: + i3c->ibi_rx_state = IBI_RX_STATE_READ_ADDR; + p_buf++; + break; + case IBI_RX_STATE_READ_ADDR: + i3c->ibi_data.addr = buf[p_buf]; + p_buf++; + i3c->ibi_rx_state = IBI_RX_STATE_READ_RNW; + break; + case IBI_RX_STATE_READ_RNW: + i3c->ibi_data.is_recv = buf[p_buf]; + p_buf++; + i3c->ibi_rx_state = IBI_RX_STATE_READ_SIZE; + break; + case IBI_RX_STATE_READ_SIZE: + i3c->ibi_data.num_bytes |= ((uint32_t)buf[p_buf] << + (8 * i3c->ibi_bytes_rxed)); + i3c->ibi_bytes_rxed++; + p_buf++; + /* + * If we're done reading the num_bytes portion, move on to reading + * data. + */ + if (i3c->ibi_bytes_rxed == sizeof(i3c->ibi_data.num_bytes)) { + i3c->ibi_data.num_bytes = le32_to_cpu(i3c->ibi_data.num_bytes); + i3c->ibi_bytes_rxed = 0; + i3c->ibi_rx_state = IBI_RX_STATE_READ_DATA; + /* If there's no data to read, we're done. */ + if (i3c->ibi_data.num_bytes == 0) { + /* + * Sanity check to see if the remote target is doing + * something wonky. This would only happen if it sends + * another IBI before the first one has been ACKed/NACKed + * by the controller. + * We'll try to recover by just exiting early and discarding + * the leftover bytes. + */ + if (p_buf < size) { + qemu_log_mask(LOG_GUEST_ERROR, "%s-%s: Remote target " + "sent trailing bytes at the end of the " + "IBI request.", + object_get_canonical_path(OBJECT(i3c)), + i3c->cfg.name); + return; + } + i3c->ibi_rx_state = IBI_RX_STATE_DONE; + } else { + /* + * We have IBI bytes to read, allocate memory for it. + * Freed when we're done sending the IBI to the controller. + */ + i3c->ibi_data.data = g_new0(uint8_t, + i3c->ibi_data.num_bytes); + } + } + break; + case IBI_RX_STATE_READ_DATA: + i3c->ibi_data.data[i3c->ibi_bytes_rxed] = buf[p_buf]; + i3c->ibi_bytes_rxed++; + p_buf++; + if (i3c->ibi_bytes_rxed == i3c->ibi_data.num_bytes) { + /* + * Sanity check to see if the remote target is doing something + * wonky. This would only happen if it sends another IBI before + * the first one has been ACKed/NACKed by the controller. + * We'll try to recover by just exiting early and discarding the + * leftover bytes. + */ + if (p_buf < size) { + qemu_log_mask(LOG_GUEST_ERROR, "%s-%s: Remote target " + "sent trailing bytes at the end of the " + "IBI request.", + object_get_canonical_path(OBJECT(i3c)), i3c->cfg.name); + return; + } + i3c->ibi_rx_state = IBI_RX_STATE_DONE; + } + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s-%s: Remote target IBI state " + "machine reached unknown state 0x%x\n", + object_get_canonical_path(OBJECT(i3c)), i3c->cfg.name, + i3c->ibi_rx_state); + g_assert_not_reached(); + } + } +} + +static void remote_i3c_ibi_rx_state_reset(RemoteI3C *i3c) +{ + if (i3c->ibi_data.num_bytes) { + free(i3c->ibi_data.data); + } + i3c->ibi_data.addr = 0; + i3c->ibi_data.is_recv = 0; + i3c->ibi_data.num_bytes = 0; + i3c->ibi_bytes_rxed = 0; + i3c->ibi_rx_state = IBI_RX_STATE_DONE; +} + +static void remote_i3c_do_ibi(RemoteI3C *i3c) +{ + uint32_t i; + uint8_t resp = REMOTE_I3C_IBI_ACK; + + trace_remote_i3c_do_ibi(i3c->cfg.name, i3c->ibi_data.addr, + i3c->ibi_data.is_recv); + if (i3c_target_send_ibi(&i3c->parent_obj, i3c->ibi_data.addr, + i3c->ibi_data.is_recv)) { + resp = REMOTE_I3C_IBI_NACK; + } else { + for (i = 0; i < i3c->ibi_data.num_bytes; i++) { + if (i3c_target_send_ibi_bytes(&i3c->parent_obj, + i3c->ibi_data.data[i])) { + resp = REMOTE_I3C_IBI_DATA_NACK; + break; + } + } + } + + if (i3c_target_ibi_finish(&i3c->parent_obj, 0x00)) { + resp = REMOTE_I3C_IBI_NACK; + } + qemu_chr_fe_write_all(&i3c->chr, &resp, sizeof(resp)); + remote_i3c_ibi_rx_state_reset(i3c); +} + +static int remote_i3c_chr_can_receive(void *opaque) +{ + return 1; +} + +static void remote_i3c_chr_receive(void *opaque, const uint8_t *buf, int size) +{ + RemoteI3C *i3c = REMOTE_I3C(opaque); + + /* + * The only things we expect to receive unprompted are: + * - An ACK of a previous transfer + * - A NACK of a previous transfer + * - An IBI requested by the remote target. + * - Bytes for an IBI request. + */ + /* If we're in the middle of handling an IBI request, parse it here. */ + if (i3c->ibi_rx_state != IBI_RX_STATE_DONE) { + remote_i3c_rx_ibi(i3c, buf, size); + /* If we finished reading the IBI, do it. */ + if (i3c->ibi_rx_state == IBI_RX_STATE_DONE) { + remote_i3c_do_ibi(i3c); + } + return; + } + + switch (buf[0]) { + case REMOTE_I3C_RX_ACK: + break; + case REMOTE_I3C_RX_NACK: + qemu_log_mask(LOG_GUEST_ERROR, "%s-%s: Received NACK from remote " + "target\n", object_get_canonical_path(OBJECT(i3c)), + i3c->cfg.name); + break; + case REMOTE_I3C_IBI: + remote_i3c_rx_ibi(i3c, buf, size); + /* If we finished reading the IBI, do it. */ + if (i3c->ibi_rx_state == IBI_RX_STATE_DONE) { + remote_i3c_do_ibi(i3c); + } + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s-%s: Unknown response 0x%x\n", + object_get_canonical_path(OBJECT(i3c)), i3c->cfg.name, + buf[0]); + break; + } +} + +static void remote_i3c_realize(DeviceState *dev, Error **errp) +{ + RemoteI3C *i3c = REMOTE_I3C(dev); + + fifo8_create(&i3c->tx_fifo, i3c->cfg.buf_size); + fifo8_create(&i3c->rx_fifo, i3c->cfg.buf_size); + i3c->ibi_data.data = g_new0(uint8_t, i3c->cfg.buf_size); + remote_i3c_ibi_rx_state_reset(i3c); + + qemu_chr_fe_set_handlers(&i3c->chr, remote_i3c_chr_can_receive, + remote_i3c_chr_receive, remote_i3c_chr_event, + NULL, OBJECT(i3c), NULL, true); +} + +static Property remote_i3c_props[] = { + DEFINE_PROP_CHR("chardev", RemoteI3C, chr), + DEFINE_PROP_UINT32("buf-size", RemoteI3C, cfg.buf_size, 0x10000), + DEFINE_PROP_STRING("device-name", RemoteI3C, cfg.name), + DEFINE_PROP_END_OF_LIST(), +}; + +static void remote_i3c_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + I3CTargetClass *k = I3C_TARGET_CLASS(klass); + + k->recv = &remote_i3c_recv; + k->send = &remote_i3c_send; + k->event = &remote_i3c_event; + k->handle_ccc_read = &remote_i3c_handle_ccc_read; + k->handle_ccc_write = &remote_i3c_handle_ccc_write; + device_class_set_props(dc, remote_i3c_props); + dc->realize = remote_i3c_realize; +} + +static const TypeInfo remote_i3c_type = { + .name = TYPE_REMOTE_I3C, + .parent = TYPE_I3C_TARGET, + .instance_size = sizeof(RemoteI3C), + .class_size = sizeof(I3CTargetClass), + .class_init = remote_i3c_class_init, +}; + +static void remote_i3c_register(void) +{ + type_register_static(&remote_i3c_type); +} + +type_init(remote_i3c_register) diff --git a/hw/i3c/trace-events b/hw/i3c/trace-events index 74c5a5dd6e..ff5efac784 100644 --- a/hw/i3c/trace-events +++ b/hw/i3c/trace-events @@ -43,3 +43,10 @@ mock_target_handle_ccc_read(uint32_t num_read, uint32_t num_to_read) "I3C mock t mock_target_new_ccc(uint8_t ccc) "I3C mock target handle CCC 0x%" PRIx8 mock_target_handle_ccc_write(uint32_t num_sent, uint32_t num_to_send) "I3C mock target send %" PRId32 "/%" PRId32 " bytes" mock_target_do_ibi(uint8_t address, bool is_recv) "I3C mock target IBI with address 0x%" PRIx8 " RnW=%d" + +# remote-i3c.c +remote_i3c_recv(const char *name, uint32_t num_read, uint32_t num_to_read) "Remote I3C '%s' read %" PRId32 "/%" PRId32 " bytes" +remote_i3c_send(const char *name, uint32_t num_sent, bool is_ccc) "Remote I3C '%s' send %" PRId32 " bytes, is_ccc=%d" +remote_i3c_ccc_read(const char *name, uint32_t num_read, uint32_t num_to_read) "Remote I3C '%s' CCC read %" PRId32 "/%" PRId32 " bytes" +remote_i3c_event(const char *name, uint8_t event) "Remote I3C '%s' event 0x%" PRIx8 +remote_i3c_do_ibi(const char *name, uint8_t addr, bool is_recv) "Remote I3C '%s' IBI with addr 0x%" PRIx8 " RnW=%d" diff --git a/include/hw/i3c/remote-i3c.h b/include/hw/i3c/remote-i3c.h new file mode 100644 index 0000000000..822b358098 --- /dev/null +++ b/include/hw/i3c/remote-i3c.h @@ -0,0 +1,72 @@ +/* + * Remote I3C Device + * + * Copyright (c) 2023 Google LLC + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 General Public License + * for more details. + */ + +/* + * The remote I3C protocol is as follows: + * On an I3C private and CCC TX (controller -> target) + * - 1-byte opcode + * - 4-byte number of bytes in the packet as a LE uint32 + * - n-byte payload + * + * On an I3C private and CCC RX (target -> controller) + * Controller to target: + * - 1-byte opcode + * - 4-byte number of bytes to read as a LE uint32 + * Remote target response: + * - 4-byte number of bytes in the packet as a LE uint32 + * - n-byte payload + * + * IBI (target -> controller, initiated by target) + * - 1-byte opcode + * - 1-byte IBI address + * - 1-byte RnW boolean + * - 4-byte length of IBI payload from target as a LE uint32 (can be 0) + * - n-byte IBI payload + */ + +#ifndef REMOTE_I3C_H_ +#define REMOTE_I3C_H_ + +#define TYPE_REMOTE_I3C "remote-i3c" +#define REMOTE_I3C(obj) OBJECT_CHECK(RemoteI3C, (obj), TYPE_REMOTE_I3C) + +/* 1-byte IBI addr, 1-byte is recv, 4-byte data len. */ +#define REMOTE_I3C_IBI_HDR_LEN 6 + +/* Stored in a uint8_t */ +typedef enum { + /* Sent from us to remote target. */ + REMOTE_I3C_START_RECV = 1, + REMOTE_I3C_START_SEND = 2, + REMOTE_I3C_STOP = 3, + REMOTE_I3C_NACK = 4, + REMOTE_I3C_RECV = 5, + REMOTE_I3C_SEND = 6, + REMOTE_I3C_HANDLE_CCC_WRITE = 7, + REMOTE_I3C_HANDLE_CCC_READ = 8, + REMOTE_I3C_IBI = 9, + /* Sent from remote target to us. */ + REMOTE_I3C_IBI_ACK = 0xc0, + REMOTE_I3C_IBI_NACK = 0xc1, + REMOTE_I3C_IBI_DATA_NACK = 0xc2, +} RemoteI3CCommand; + +typedef enum { + REMOTE_I3C_RX_ACK = 0, + REMOTE_I3C_RX_NACK = 1, +} RemoteI3CRXEvent; + +#endif From patchwork Fri Mar 31 01:01:30 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Joe Komlodi X-Patchwork-Id: 1763550 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=nongnu.org (client-ip=209.51.188.17; helo=lists.gnu.org; envelope-from=qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org; receiver=) Authentication-Results: legolas.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=google.com header.i=@google.com header.a=rsa-sha256 header.s=20210112 header.b=aOtVT/Hn; dkim-atps=neutral Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-ECDSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4Pnhpm2nPTz1yYr for ; Fri, 31 Mar 2023 12:02:40 +1100 (AEDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1pi39Z-0003MH-4G; Thu, 30 Mar 2023 21:02:05 -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 <3BjEmZAcKCikPTRQTINLTTLQJ.HTRVJRZ-IJaJQSTSLSZ.TWL@flex--komlodi.bounces.google.com>) id 1pi39X-0003Kg-FQ for qemu-devel@nongnu.org; Thu, 30 Mar 2023 21:02:03 -0400 Received: from mail-yw1-x1149.google.com ([2607:f8b0:4864:20::1149]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from <3BjEmZAcKCikPTRQTINLTTLQJ.HTRVJRZ-IJaJQSTSLSZ.TWL@flex--komlodi.bounces.google.com>) id 1pi39T-0006cx-TS for qemu-devel@nongnu.org; Thu, 30 Mar 2023 21:02:03 -0400 Received: by mail-yw1-x1149.google.com with SMTP id 00721157ae682-541a39df9f4so207039987b3.20 for ; Thu, 30 Mar 2023 18:01:59 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20210112; t=1680224518; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=VbKo8BedI/4E1LOAbKY+WbhpFCpcANN8MC8XC0hG1jk=; b=aOtVT/Hnql97VkhTVFsRgo9EEYArsyBO1hVGaFDWxVC1R6GEddChYWVrBaJNve2bKb V1BTkFMg+DfS6w1xHgmtRC/GRgECiXpJqPBHTig/ixpjPnempx2Y9K3/4WAyqppHRAc6 CTfr29geo3uL3OP5exnia7JSDXPvO49Y1DlHFfBnVFe3Ew/rnjF31uw8FZ6WxTU8lh6Z sUS6i1SRNy5AHa6hAIdEqZhJh68zsGs9lzauP0SVTwP8y9+Rm9+f3148ibHHkZKD471T 1z2W8+bw8p5MrYj7HxTudqf5iZ7snIZjZiK95zVR52cW8vmVmTeHJDyvbSOaud8ySx64 yHIA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; t=1680224518; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=VbKo8BedI/4E1LOAbKY+WbhpFCpcANN8MC8XC0hG1jk=; b=foeCavz08jqHWzlfoyJKvrr+7TFZ1nNThTnEYvS8b/ilx8mbJkvRdyrg6Rysft/3pM u/WpyeBTHacM6V+s6EiADyN5U3Y2H7/xwyYZ52SLzmnS/7UiPh1Jz2b+1e3OCsXeZQWJ tvVRf7jbo6l2NYv7jejyivqom7yAIw44yv2PFgbl69UY3jQtNT/GcJV6RxYTaUVNTF1/ iso3IturvGcuts4fPUUKbJJaj1NykfAsC+iQecSRqxFxLds14XAAipB6d9MPy7SCPPQs oAHS6Llp+7jGJaMTx5b3V5aBj+ZSNkFQAiSyaYJrbIMiwToGk4mslk9CFplSZGu1M2Vv xZag== X-Gm-Message-State: AAQBX9f2eFVTSiO+AchXI8vrqMeTZNy6yAnP8Jbz8hss47etzc2UMxV1 p7hGTOAouWxe/8pVlCdArYcgLT8PM9/my3HW4ml5kFWYe9OWBH9IaJpj/BYEnf3+7+lA7IM6xzi IBldKy7n85Rgq7DFTDQzzgiamVRSWdzxnXIswC+YfB+4fpZqEsLgErdNHAc2iPMc= X-Google-Smtp-Source: AKy350ZSLJTFoiDS5Xg7OoDgzVtCnkcNOb1HdRrY4xMDGvZ/FFuI150xh8qY2Mm09tQUglUYcQIAsDWT4Gtq X-Received: from komlodi.c.googlers.com ([fda3:e722:ac3:cc00:7f:e700:c0a8:35ee]) (user=komlodi job=sendgmr) by 2002:a05:6902:1004:b0:b75:968e:f282 with SMTP id w4-20020a056902100400b00b75968ef282mr16619718ybt.11.1680224518376; Thu, 30 Mar 2023 18:01:58 -0700 (PDT) Date: Fri, 31 Mar 2023 01:01:30 +0000 In-Reply-To: <20230331010131.1412571-1-komlodi@google.com> Mime-Version: 1.0 References: <20230331010131.1412571-1-komlodi@google.com> X-Mailer: git-send-email 2.40.0.348.gf938b09366-goog Message-ID: <20230331010131.1412571-16-komlodi@google.com> Subject: [PATCH 15/16] qtest: remote_i3c: Add remote I3C qtest From: Joe Komlodi To: qemu-devel@nongnu.org Cc: venture@google.com, komlodi@google.com, peter.maydell@linaro.org Received-SPF: pass client-ip=2607:f8b0:4864:20::1149; envelope-from=3BjEmZAcKCikPTRQTINLTTLQJ.HTRVJRZ-IJaJQSTSLSZ.TWL@flex--komlodi.bounces.google.com; helo=mail-yw1-x1149.google.com X-Spam_score_int: -95 X-Spam_score: -9.6 X-Spam_bar: --------- X-Spam_report: (-9.6 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_MED=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, USER_IN_DEF_DKIM_WL=-7.5 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+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org This adds a remote I3C qtest using the Aspeed I3C controller and an AST2600 board. The qtest uses a basic Aspeed I3C driver to test: - data transmission from controller to target - data reception from target to controller - target sending an IBI (with data bytes) to controller Signed-off-by: Joe Komlodi Reviewed-by: Hao Wu Reviewed-by: Patrick Venture --- hw/arm/Kconfig | 1 + tests/qtest/meson.build | 1 + tests/qtest/remote-i3c-test.c | 610 ++++++++++++++++++++++++++++++++++ 3 files changed, 612 insertions(+) create mode 100644 tests/qtest/remote-i3c-test.c diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index a6ca5a9c55..fa67769d1d 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -455,6 +455,7 @@ config ASPEED_SOC select FTGMAC100 select I2C select I3C + select I3C_DEVICES select DPS310 select PCA9552 select SERIAL diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index 85ea4e8d99..480e11558e 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -190,6 +190,7 @@ qtests_npcm7xx = \ qtests_aspeed = \ ['aspeed_hace-test', 'aspeed_smc-test', + 'remote-i3c-test', 'aspeed_gpio-test'] qtests_arm = \ (config_all_devices.has_key('CONFIG_MPS2') ? ['sse-timer-test'] : []) + \ diff --git a/tests/qtest/remote-i3c-test.c b/tests/qtest/remote-i3c-test.c new file mode 100644 index 0000000000..49c993ea64 --- /dev/null +++ b/tests/qtest/remote-i3c-test.c @@ -0,0 +1,610 @@ +/* + * QTest testcase for the remote I3C device, using the AST2600 I3C controller. + * + * Copyright 2023 Google LLC + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 General Public License + * for more details. + */ + +#include "qemu/osdep.h" +#include "libqtest-single.h" +#include "hw/registerfields.h" +#include "hw/i3c/i3c.h" +#include "hw/i3c/remote-i3c.h" +#include "hw/i3c/aspeed_i3c.h" + +/* Starting address of the AST2600 I3C block. */ +#define ASPEED_I3C_BASE 0x1e7a0000 +/* Offset to the first controller in the block. */ +#define ASPEED_I3C_CONTROLLER_OFFSET 0x2000 +#define I3C(x) (ASPEED_I3C_BASE + ASPEED_I3C_CONTROLLER_OFFSET + ((x) * 0x1000)) +#define TARGET_ADDR 0x10 + +/* I3C Device Registers */ +REG32(DEVICE_CTRL, 0x00) + FIELD(DEVICE_CTRL, I3C_BROADCAST_ADDR_INC, 0, 1) + FIELD(DEVICE_CTRL, I2C_SLAVE_PRESENT, 7, 1) + FIELD(DEVICE_CTRL, HOT_JOIN_ACK_NACK_CTRL, 8, 1) + FIELD(DEVICE_CTRL, IDLE_CNT_MULTIPLIER, 24, 2) + FIELD(DEVICE_CTRL, SLV_ADAPT_TO_I2C_I3C_MODE, 27, 1) + FIELD(DEVICE_CTRL, DMA_HANDSHAKE_EN, 28, 1) + FIELD(DEVICE_CTRL, I3C_ABORT, 29, 1) + FIELD(DEVICE_CTRL, I3C_RESUME, 30, 1) + FIELD(DEVICE_CTRL, I3C_EN, 31, 1) +REG32(COMMAND_QUEUE_PORT, 0x0c) + FIELD(COMMAND_QUEUE_PORT, CMD_ATTR, 0, 3) + /* Transfer command structure */ + FIELD(COMMAND_QUEUE_PORT, TID, 3, 4) + FIELD(COMMAND_QUEUE_PORT, CMD, 7, 8) + FIELD(COMMAND_QUEUE_PORT, CP, 15, 1) + FIELD(COMMAND_QUEUE_PORT, DEV_INDEX, 16, 5) + FIELD(COMMAND_QUEUE_PORT, SPEED, 21, 3) + FIELD(COMMAND_QUEUE_PORT, ROC, 26, 1) + FIELD(COMMAND_QUEUE_PORT, SDAP, 27, 1) + FIELD(COMMAND_QUEUE_PORT, RNW, 28, 1) + FIELD(COMMAND_QUEUE_PORT, TOC, 30, 1) + FIELD(COMMAND_QUEUE_PORT, PEC, 31, 1) + /* Transfer argument data structure */ + FIELD(COMMAND_QUEUE_PORT, DB, 8, 8) + FIELD(COMMAND_QUEUE_PORT, DL, 16, 16) + /* Short data argument data structure */ + FIELD(COMMAND_QUEUE_PORT, BYTE_STRB, 3, 3) + FIELD(COMMAND_QUEUE_PORT, BYTE0, 8, 8) + FIELD(COMMAND_QUEUE_PORT, BYTE1, 16, 8) + FIELD(COMMAND_QUEUE_PORT, BYTE2, 24, 8) + /* Address assignment command structure */ + /* + * bits 3..21 and 26..31 are the same as the transfer command structure, or + * marked as reserved. + */ + FIELD(COMMAND_QUEUE_PORT, DEV_COUNT, 21, 3) +REG32(RESPONSE_QUEUE_PORT, 0x10) + FIELD(RESPONSE_QUEUE_PORT, DL, 0, 16) + FIELD(RESPONSE_QUEUE_PORT, CCCT, 16, 8) + FIELD(RESPONSE_QUEUE_PORT, TID, 24, 4) + FIELD(RESPONSE_QUEUE_PORT, ERR_STATUS, 28, 4) +REG32(RX_TX_DATA_PORT, 0x14) +REG32(IBI_QUEUE_STATUS, 0x18) + FIELD(IBI_QUEUE_STATUS, IBI_DATA_LEN, 0, 8) + FIELD(IBI_QUEUE_STATUS, IBI_ID, 8, 8) + FIELD(IBI_QUEUE_STATUS, IBI_RX_STATUS, 28, 4) +REG32(IBI_QUEUE_DATA, 0x18) +REG32(QUEUE_STATUS_LEVEL, 0x4c) + FIELD(QUEUE_STATUS_LEVEL, CMD_QUEUE_EMPTY_LOC, 0, 8) + FIELD(QUEUE_STATUS_LEVEL, RESP_BUF_BLR, 8, 8) + FIELD(QUEUE_STATUS_LEVEL, IBI_BUF_BLR, 16, 8) + FIELD(QUEUE_STATUS_LEVEL, IBI_STATUS_CNT, 24, 5) +REG32(DATA_BUFFER_STATUS_LEVEL, 0x50) + FIELD(DATA_BUFFER_STATUS_LEVEL, TX_BUF_EMPTY_LOC, 0, 8) + FIELD(DATA_BUFFER_STATUS_LEVEL, RX_BUF_BLR, 16, 8) +/* Dev addr table fields */ +REG32(DEVICE_ADDR_TABLE_LOC1, 0x280) + FIELD(DEVICE_ADDR_TABLE_LOC1, DEV_DYNAMIC_ADDR, 16, 8) + +typedef union AspeedI3CResponse { + uint32_t word; + uint16_t data_len; + uint8_t ccc_type; + uint8_t tid:4; + uint8_t err:4; +} AspeedI3CResponse; + +static int sock; +static int fd; + +static in_port_t open_socket(void) +{ + struct sockaddr_in myaddr; + struct timeval timeout = { .tv_sec = 1, }; + socklen_t addrlen; + + myaddr.sin_family = AF_INET; + myaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + myaddr.sin_port = 0; + sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + g_assert(sock != -1); + g_assert(bind(sock, (struct sockaddr *) &myaddr, sizeof(myaddr)) != -1); + setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)); + + addrlen = sizeof(myaddr); + g_assert(getsockname(sock, (struct sockaddr *) &myaddr , &addrlen) != -1); + g_assert(listen(sock, 1) != -1); + return ntohs(myaddr.sin_port); +} + +static void setup_fd(void) +{ + fd_set readfds; + + FD_ZERO(&readfds); + FD_SET(sock, &readfds); + g_assert(select(sock + 1, &readfds, NULL, NULL, NULL) == 1); + + fd = accept(sock, NULL, 0); +} + +static AspeedI3CTransferCmd aspeed_i3c_create_xfer_cmd(uint8_t cmd, + uint8_t dev_index, + bool rnw, + bool dbp) +{ + return ((AspeedI3CTransferCmd) { + .cmd_attr = ASPEED_I3C_CMD_ATTR_TRANSFER_CMD, + .tid = 0x01, + .cmd = cmd, + .cp = (cmd != 0) ? 1 : 0, + .dev_index = dev_index, + .speed = 0, /* SDR */ + .dbp = dbp, + .roc = 1, + .sdap = (cmd == 0x02) ? 1 : 0, /* Short data arg present. */ + .rnw = rnw, + .toc = 1, + .pec = 0 + }); +} + +static AspeedI3CTransferArg aspeed_i3c_create_xfer_arg(uint8_t db, + uint16_t data_len) +{ + return ((AspeedI3CTransferArg) { + .cmd_attr = ASPEED_I3C_CMD_ATTR_TRANSFER_ARG, + .db = db, + .data_len = data_len, + }); +} + +static void aspeed_i3c_enable(uint32_t base) +{ + uint32_t val = readl(base + A_DEVICE_CTRL); + val = FIELD_DP32(val, DEVICE_CTRL, I3C_RESUME, 1); + val = FIELD_DP32(val, DEVICE_CTRL, I3C_EN, 1); + writel(base + A_DEVICE_CTRL, val); + /* + * Sanity check the enable write. I3C_RESUME is auto-cleared so don't + * check it. + */ + g_assert(readl(base + A_DEVICE_CTRL) & R_DEVICE_CTRL_I3C_EN_MASK); +} + +static AspeedI3CResponse aspeed_i3c_read_resp(uint32_t base) +{ + AspeedI3CResponse resp; + uint32_t queue_status = readl(base + A_QUEUE_STATUS_LEVEL); + /* No response to read. */ + if (FIELD_EX32(queue_status, QUEUE_STATUS_LEVEL, RESP_BUF_BLR) == 0) { + resp.word = 0; + } else { + resp.word = readl(base + A_DEVICE_CTRL); + } + return resp; +} + +static void aspeed_i3c_send(uint32_t base, uint8_t dev_index, + const uint32_t *data, uint16_t len) +{ + AspeedI3CCmdQueueData cmd; + AspeedI3CCmdQueueData arg; + uint16_t words_txed = 0; + + /* Start doing the transfer. */ + while (words_txed < len) { + /* Push data to the TX queue. */ + uint32_t tx_num_empty = FIELD_EX32(readl(base + + A_DATA_BUFFER_STATUS_LEVEL), + DATA_BUFFER_STATUS_LEVEL, + TX_BUF_EMPTY_LOC); + for (uint16_t i = 0; i < tx_num_empty; i++) { + writel(base + A_RX_TX_DATA_PORT, data[words_txed]); + words_txed++; + /* We have no more to transfer, bail early. */ + if (words_txed >= len) { + break; + } + } + + /* Now that the data is in the queue, we can start our transfer. */ + /* + * CMD is ignored due to this not being a CCC, and there is no defining + * byte, also because this isn't a CCC. + */ + cmd.transfer_cmd = aspeed_i3c_create_xfer_cmd(0, dev_index, false, + false); + arg.transfer_arg = aspeed_i3c_create_xfer_arg(0, len * sizeof(*data)); + /* Order to push is arg then command. */ + writel(base + A_COMMAND_QUEUE_PORT, arg.word); + writel(base + A_COMMAND_QUEUE_PORT, cmd.word); + } +} + +static void aspeed_i3c_send_ccc(uint32_t base, uint8_t ccc_cmd) +{ + AspeedI3CCmdQueueData cmd; + AspeedI3CCmdQueueData arg; + + cmd.transfer_cmd = aspeed_i3c_create_xfer_cmd(ccc_cmd, 0, false, + false); + arg.transfer_arg = aspeed_i3c_create_xfer_arg(0, 0); + /* Order to push is arg then command. */ + writel(base + A_COMMAND_QUEUE_PORT, arg.word); + writel(base + A_COMMAND_QUEUE_PORT, cmd.word); +} + +static void aspeed_i3c_recv(uint32_t base, uint8_t dev_index, uint8_t *data, + uint16_t len) +{ + AspeedI3CCmdQueueData cmd; + AspeedI3CCmdQueueData arg; + uint16_t bytes_rxed = 0; + uint32_t *p32_data = (uint32_t *)data; + + /* Start doing the transfer. */ + while (bytes_rxed < len) { + /* Send the RX request. */ + /* + * CMD is ignored due to this not being a CCC, and there is no defining + * byte, also because this isn't a CCC. + */ + cmd.transfer_cmd = aspeed_i3c_create_xfer_cmd(0, dev_index, true, + false); + uint16_t num_to_rx = (len - bytes_rxed) > ASPEED_I3C_RX_QUEUE_CAPACITY ? + ASPEED_I3C_RX_QUEUE_CAPACITY : (len - bytes_rxed); + arg.transfer_arg = aspeed_i3c_create_xfer_arg(0, num_to_rx); + /* Order to push is arg then command. */ + writel(base + A_COMMAND_QUEUE_PORT, arg.word); + writel(base + A_COMMAND_QUEUE_PORT, cmd.word); + + /* Read the data from the data RX queue. */ + uint32_t rx_word_num = + FIELD_EX32(readl(base + A_DATA_BUFFER_STATUS_LEVEL), + DATA_BUFFER_STATUS_LEVEL, RX_BUF_BLR); + for (uint16_t i = 0; i < rx_word_num; i++) { + *p32_data = readl(base + A_RX_TX_DATA_PORT); + p32_data++; + bytes_rxed += 4; + } + } +} + +static void assert_good_resp(uint32_t base) +{ + /* We expect a good response from this. */ + AspeedI3CResponse resp = aspeed_i3c_read_resp(base); + g_assert(resp.err == ASPEED_I3C_RESP_QUEUE_ERR_NONE); +} + +static void read_data(uint8_t *data, size_t len) +{ + ssize_t ret; + size_t len_read = 0; + + while (len_read < len) { + ret = read(fd, &data[len_read], len); + g_assert(ret != -1); + len_read += ret; + } +} + +static void remote_i3c_read_and_verify(const uint8_t *expected_data, size_t len) +{ + g_autofree uint8_t *data_read = g_new0(uint8_t, len); + + read_data(data_read, len); + g_assert(memcmp(data_read, expected_data, len) == 0); +} + +static void add_targets_to_bus(uint32_t base) +{ + /* Arbitrary large enough size. */ + uint8_t remote_target_expected_data[8]; + + /* Send SATAASA to the remote target. */ + aspeed_i3c_send_ccc(base, I3C_CCC_SETAASA); + /* + * Verify everything is good. + * The remote target should receive: + * - an I3C_START event + * - the size of the CCC packet as a LE uint32 + * - the CCC + * - then an I3C_STOP event. + * The controller should have a good response in the queue. + */ + assert_good_resp(base); + remote_target_expected_data[0] = REMOTE_I3C_START_SEND; + remote_target_expected_data[1] = REMOTE_I3C_HANDLE_CCC_WRITE; + uint32_t *p32 = (uint32_t *)&remote_target_expected_data[2]; + *p32 = htole32(1); + remote_target_expected_data[6] = I3C_CCC_SETAASA; + remote_target_expected_data[7] = REMOTE_I3C_STOP; + remote_i3c_read_and_verify(remote_target_expected_data, 8); + + /* + * Populate the device table. On a real system we would either: + * - populate the table and send ENTDAA, then probe the addresses to see who + * exists. + * - SETAASA and then go through a list addresses to see who exists, probe + * them, and add them to the table. + * We're doing the SETAASA way, minus the probing portion, so just add the + * known address to the table. + */ + uint32_t val = 0; + val = FIELD_DP32(val, DEVICE_ADDR_TABLE_LOC1, DEV_DYNAMIC_ADDR, + TARGET_ADDR); + writel(base + A_DEVICE_ADDR_TABLE_LOC1, val); +} + +static void send_and_verify(uint32_t i3c_base, const uint32_t *data, size_t len) +{ + /* + * Add padding to the data_read packet, since the remote target will receive + * extra bytes that include the I3C START and STOP events, along with the + * length of the packet, and the data packet itself. + */ + uint32_t data_size = len * sizeof(*data); + size_t expected_data_len = data_size + 7; + g_autofree uint8_t *remote_target_expected_data = g_new0(uint8_t, + expected_data_len); + remote_target_expected_data[0] = REMOTE_I3C_START_SEND; + remote_target_expected_data[1] = REMOTE_I3C_SEND; + uint32_t *p32 = (uint32_t *)&remote_target_expected_data[2]; + *p32 = htole32(data_size); + memcpy(&remote_target_expected_data[6], data, data_size); + remote_target_expected_data[data_size + 6] = REMOTE_I3C_STOP; + + aspeed_i3c_send(i3c_base, 0, data, len); + assert_good_resp(i3c_base); + remote_i3c_read_and_verify(remote_target_expected_data, expected_data_len); +} + +/* Remote target RX, e.g. controller -> target. */ +static void test_remote_i3c_rx(gconstpointer test_data) +{ + uint32_t controller_num = *(uint32_t *)test_data; + uint32_t i3c_base = I3C(controller_num); + /* + * The Aspeed controller expects data in 32-bit words, so make this 32-bits. + */ + const uint32_t data[] = {7, 6, 5, 4, 3, 2, 1, 0}; + /* Enable the controller. */ + aspeed_i3c_enable(i3c_base); + /* + * Tell the target to use its static address as its dynamic address, and + * populate the device table. + */ + add_targets_to_bus(i3c_base); + /* Now we can test sending data to the target. */ + send_and_verify(i3c_base, data, ARRAY_SIZE(data)); +} + +static void read_and_verify(uint32_t i3c_base, const uint8_t *data, size_t len) +{ + g_autofree uint8_t *data_received = g_new0(uint8_t, len); + + /* Send the I3C recv request. */ + aspeed_i3c_recv(i3c_base, 0, data_received, len); + /* + * Verify everything is okay. Anything on the remote I3C protocol level is + * handled by the remote target thread. We just need to check that we + * received what we expected. + */ + assert_good_resp(i3c_base); + g_assert(memcmp(data_received, data, len) == 0); +} + +static void *remote_target_thread(void *arg) +{ + uint8_t byte; + uint32_t bytes_to_send; + uint32_t bytes_to_send_le; + const uint8_t *data = (const uint8_t *)arg; + + /* Loop forever reading and parsing incoming data. */ + while (1) { + /* + * We can error out during program teardown due to the socket closing, + * so don't assert. + * If this has a proper error during test, the main thread will error + * due to the target thread (this one) not sending anything. + */ + if (read(fd, &byte, 1) != 1) { + break; + } + + switch (byte) { + case REMOTE_I3C_START_RECV: + case REMOTE_I3C_STOP: + /* Don't care, do nothing. */ + break; + case REMOTE_I3C_RECV: + /* Read in the number of bytes the controller wants. */ + g_assert(read(fd, &bytes_to_send_le, sizeof(bytes_to_send_le)) == + sizeof(bytes_to_send_le)); + bytes_to_send = le32toh(bytes_to_send_le); + + /* + * Send the data. We first send the number of bytes we're sending as + * a uint32 LE word (which is the same as the number of bytes the + * controller is expecting), followed by the data. + */ + g_assert(write(fd, (uint8_t *)&bytes_to_send_le, + sizeof(bytes_to_send_le)) == + sizeof(bytes_to_send_le)); + g_assert(write(fd, data, bytes_to_send) == bytes_to_send); + break; + default: + g_printerr("Remote target received unknown byte 0x%.2x\n", byte); + g_assert_not_reached(); + } + } + + return NULL; +} + +/* Remote target TX, e.g. target -> controller. */ +static void test_remote_i3c_tx(gconstpointer test_data) +{ + uint32_t controller_num = *(uint32_t *)test_data; + uint32_t i3c_base = I3C(controller_num); + /* Non-const since the thread prototype needs a non-const pointer. */ + uint8_t data[] = {7, 6, 5, 4, 3, 2, 1, 0}; + GThread *target_thread; + /* Enable the controller. */ + aspeed_i3c_enable(i3c_base); + /* + * Tell the target to use its static address as its dynamic address, and + * populate the device table. + */ + add_targets_to_bus(i3c_base); + + /* + * Now we can test receiving data from the target. + * The target will need to respond while the controller is doing the I3C + * receive (meaning we will be blocked on the remote target sending data to + * us), so we need to make a separate thread for the remote target to send + * data to the controller. + */ + target_thread = g_thread_new("remote-target", remote_target_thread, data); + read_and_verify(i3c_base, data, ARRAY_SIZE(data)); + g_thread_join(target_thread); +} + +static void remote_i3c_ibi(const uint32_t *data, uint32_t len) +{ + /* Convert len to bytes to make math cleaner. */ + len *= sizeof(uint32_t); + /* + * IBI format is: + * - 1-byte REMOTE_I3C_IBI request. + * - 1-byte address of target sending the IBI. + * - 1-byte RnW bit. + * - 4-byte size of IBI payload. + * - n-byte IBI payload. + */ + size_t ibi_req_size = 7 + len; + g_autofree uint8_t *ibi_req = g_new0(uint8_t, ibi_req_size); + uint32_t len_le; + uint8_t ibi_resp; + + ibi_req[0] = REMOTE_I3C_IBI; + ibi_req[1] = TARGET_ADDR; + ibi_req[2] = 0; /* RnW = 0 to make this a target interrupt request. */ + len_le = htole32(len); + memcpy(&ibi_req[3], &len_le, sizeof(len_le)); + memcpy(&ibi_req[7], data, len); + + /* Send the request and read back the ACK. */ + g_assert(write(fd, ibi_req, ibi_req_size) == ibi_req_size); + g_assert(read(fd, &ibi_resp, sizeof(ibi_resp)) == sizeof(ibi_resp)); + g_assert(ibi_resp == REMOTE_I3C_IBI_ACK); +} + +static void aspeed_i3c_read_ibi_and_verify(uint32_t i3c_base, + const uint32_t *data, size_t len) +{ + g_autofree uint32_t *ibi_data = g_new0(uint32_t, len * sizeof(uint32_t)); + + /* Make sure there's actually something to read in the IBI queue. */ + uint8_t ibi_buf_lvl = FIELD_EX32(readl(i3c_base + A_QUEUE_STATUS_LEVEL), + QUEUE_STATUS_LEVEL, IBI_BUF_BLR); + /* + * ibi_buf_level should have 1-byte for IBI status, plus data size in words. + */ + g_assert(ibi_buf_lvl == 1 + len); + uint32_t ibi_status = readl(i3c_base + A_IBI_QUEUE_STATUS); + /* IBI_ID is target address << 1 | RnW bit (which is 0) */ + g_assert(FIELD_EX32(ibi_status, IBI_QUEUE_STATUS, IBI_ID) == + (TARGET_ADDR << 1)); + /* IBI data length in the register is stored in bytes. */ + uint32_t ibi_data_len = FIELD_EX32(ibi_status, IBI_QUEUE_STATUS, + IBI_DATA_LEN); + g_assert(ibi_data_len == len * sizeof(uint32_t)); + + /* + * Read in the IBI bytes, if they aren't word-aligned, read in an extra + * word. + */ + for (size_t i = 0; i < (ibi_data_len / 4) + + (ibi_data_len & 0x03) ? 1 : 0; i++) { + ibi_data[i] = readl(i3c_base + A_IBI_QUEUE_DATA); + } + /* Make sure the data matches. */ + g_assert(memcmp(ibi_data, data, len) == 0); +} + +static void ibi_and_verify(uint32_t i3c_base, const uint32_t *data, size_t len) +{ + /* Send the IBI request. */ + remote_i3c_ibi(data, len); + /* Read it and verify it matches what we expect. */ + aspeed_i3c_read_ibi_and_verify(i3c_base, data, len); +} + +/* Remote target IBI. */ +static void test_remote_i3c_ibi(gconstpointer test_data) +{ + uint32_t controller_num = *(uint32_t *)test_data; + uint32_t i3c_base = I3C(controller_num); + uint32_t data = 0xaa55cc33; + /* Enable the controller. */ + aspeed_i3c_enable(i3c_base); + /* + * Tell the target to use its static address as its dynamic address, and + * populate the device table. + */ + add_targets_to_bus(i3c_base); + + /* + * To test IBIing, we will: + * - Have the target IBI the controller by writing to the socket. + * - The controller ACKs and enqueues the IBI request. + * - The ACK is sent over socket, we verify it's there. + * - We read the request from the controller IBI queue. + */ + ibi_and_verify(i3c_base, &data, 1); +} + +int main(int argc, char **argv) +{ + int ret; + int port; + /* Global register base address + offset of first controller. */ + uint32_t i3c_controller_num = 0; + g_test_init(&argc, &argv, NULL); + port = open_socket(); + + global_qtest = qtest_initf("-machine ast2600-evb " + "-chardev socket,id=remote-i3c-chr,port=%d,host=localhost " + "-device %s," + "chardev=remote-i3c-chr," + "device-name=remote-target," + "bus=aspeed.i3c.device.0," + "pid=0xfeedf00dd00d," + "dcr=0xaa," + "bcr=0x55," + "static-address=%d", + port, TYPE_REMOTE_I3C, TARGET_ADDR); + setup_fd(); + + /* Remote target RXing, i.e. controller -> target. */ + qtest_add_data_func("remote-i3c-rx", (void *)&i3c_controller_num, + test_remote_i3c_rx); + /* Remote target TXing, i.e. controller -> target. */ + qtest_add_data_func("remote-i3c-tx", (void *)&i3c_controller_num, + test_remote_i3c_tx); + /* Remote target IBIing. */ + qtest_add_data_func("remote-i3c-ibi", (void *)&i3c_controller_num, + test_remote_i3c_ibi); + + ret = g_test_run(); + qtest_end(); + + return ret; +} From patchwork Fri Mar 31 01:01:31 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Joe Komlodi X-Patchwork-Id: 1763562 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=nongnu.org (client-ip=209.51.188.17; helo=lists.gnu.org; envelope-from=qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org; receiver=) Authentication-Results: legolas.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=google.com header.i=@google.com header.a=rsa-sha256 header.s=20210112 header.b=iFV4HOPJ; dkim-atps=neutral Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-ECDSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4Pnht439wpz1yY8 for ; Fri, 31 Mar 2023 12:05:32 +1100 (AEDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1pi39X-0003LK-T1; Thu, 30 Mar 2023 21:02:03 -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 <3BzEmZAcKCioQUSRUJOMUUMRK.IUSWKSa-JKbKRTUTMTa.UXM@flex--komlodi.bounces.google.com>) id 1pi39W-0003Io-Ia for qemu-devel@nongnu.org; Thu, 30 Mar 2023 21:02:02 -0400 Received: from mail-yb1-xb4a.google.com ([2607:f8b0:4864:20::b4a]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from <3BzEmZAcKCioQUSRUJOMUUMRK.IUSWKSa-JKbKRTUTMTa.UXM@flex--komlodi.bounces.google.com>) id 1pi39V-0006d5-01 for qemu-devel@nongnu.org; Thu, 30 Mar 2023 21:02:02 -0400 Received: by mail-yb1-xb4a.google.com with SMTP id 4-20020a251904000000b00b7f75c3cafdso5283176ybz.16 for ; Thu, 30 Mar 2023 18:02:00 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20210112; t=1680224519; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=kmOrD5Vl4Y3U3Y6s4gR2tn0DFm2ngoTvHdEN5WGZh6c=; b=iFV4HOPJhcq8QsMTJo5PY+ChsGvjewzILUIQXbOEESoleXU6e9dcnJ9RpQ+KhQkgEt 8kjFLIWseT6GPoUgOf9rRBpNfWHIIvZ176kCpvPqPpwJ3WY5S/sNEcWZfCeZOvUxOhwN /FH+/0G00pwBhtzNMgTGOapR7yTwwLtli7OkUiutc10Wq3j/RTJv49LU0Yf1C77cxwVN et8aXzPtCf5pfSn+2iKrFgBiZ1oTnJfNC0WXj1+xMwn6858tCe4WfjYRNjOD3fnSWQPU BOQ0tHcRR59VdpHN9C3K7oXu2S33Ebu2uPcyz/BZSfLrAxhT5w+VvdBFOx48oTb0tTa6 +eyA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; t=1680224519; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=kmOrD5Vl4Y3U3Y6s4gR2tn0DFm2ngoTvHdEN5WGZh6c=; b=l8aO94EIjw9d1v0FpedQoPMuWlpvuplSLzWEKTDFAjoaznjG6QDtPYVL6pfdEyCdeD enFaltzQLrJsdSh0UAsHSjfls5bsBmsIRGQ9TnKva4Un4nB9FjNn9TWn2/iAqSu40V5P Y98Wt0WN/8oHYUMLixODZSb0uJ1DIchje3o8UbsiuGmxK36qxlKAelp+GoTH28YmlSTi figNYi8M8zhCheGluyL4sCtbCeH72rNNEXH7W/YwewBO0RBxGicgXhOI3b/xZPrDek9L ByRkdNM9SyhHmGrxaYcx5A/X5CbX8MNrYe/m778VAm3+dQb51L3c+WJQBA674K8SLMGs h4OA== X-Gm-Message-State: AAQBX9cXTvXHjC8KmqY0gl07rF8n9G7VAw7Sl4+IUQkfO59FAjPIDqUo fdgJSHIeKXohlcz6kG9ejIu8PzFcht1fnNdKnmBxLNw0eLgXrfGI5n7JXeo39xOKUpLQAvC9WIj fUCzV7DjKUIjegJnV+u1N4DYBqXcMC2gEz9RcUKO94Tbu1auvRxWM3ytdjUmeYZs= X-Google-Smtp-Source: AKy350Y3uBp7XH8uLzMYOyFTDesmXEfmKbpvUfPJAqHAvauTbeVA7/5qnxjcXCMZ0PeJ1LI0R29h4iYaIKZF X-Received: from komlodi.c.googlers.com ([fda3:e722:ac3:cc00:7f:e700:c0a8:35ee]) (user=komlodi job=sendgmr) by 2002:a05:6902:1549:b0:b77:be38:6406 with SMTP id r9-20020a056902154900b00b77be386406mr13272549ybu.9.1680224519758; Thu, 30 Mar 2023 18:01:59 -0700 (PDT) Date: Fri, 31 Mar 2023 01:01:31 +0000 In-Reply-To: <20230331010131.1412571-1-komlodi@google.com> Mime-Version: 1.0 References: <20230331010131.1412571-1-komlodi@google.com> X-Mailer: git-send-email 2.40.0.348.gf938b09366-goog Message-ID: <20230331010131.1412571-17-komlodi@google.com> Subject: [PATCH 16/16] hw/i3c: Add hotplug support From: Joe Komlodi To: qemu-devel@nongnu.org Cc: venture@google.com, komlodi@google.com, peter.maydell@linaro.org Received-SPF: pass client-ip=2607:f8b0:4864:20::b4a; envelope-from=3BzEmZAcKCioQUSRUJOMUUMRK.IUSWKSa-JKbKRTUTMTa.UXM@flex--komlodi.bounces.google.com; helo=mail-yb1-xb4a.google.com X-Spam_score_int: -95 X-Spam_score: -9.6 X-Spam_bar: --------- X-Spam_report: (-9.6 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_MED=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, USER_IN_DEF_DKIM_WL=-7.5 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+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org This adds support for hotplugging in I3C. Conceptually this can be thought of as an I3C target being physically socketed onto a board. It is then the target's responsibility to go through the hot-join and DAA process so it can participate on the bus. Signed-off-by: Joe Komlodi Reviewed-by: Patrick Venture --- hw/i3c/core.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/hw/i3c/core.c b/hw/i3c/core.c index 758dd7141f..c6ef16bbba 100644 --- a/hw/i3c/core.c +++ b/hw/i3c/core.c @@ -19,6 +19,7 @@ #include "qapi/error.h" #include "trace.h" #include "hw/i3c/i3c.h" +#include "hw/hotplug.h" #include "hw/qdev-properties.h" static Property i3c_props[] = { @@ -29,11 +30,27 @@ static Property i3c_props[] = { DEFINE_PROP_END_OF_LIST(), }; +static void i3c_realize(BusState *bus, Error **errp) +{ + qbus_set_bus_hotplug_handler(bus); +} + +static void i3c_class_init(ObjectClass *klass, void *data) +{ + BusClass *k = BUS_CLASS(klass); + k->realize = i3c_realize; +} + static const TypeInfo i3c_bus_info = { .name = TYPE_I3C_BUS, .parent = TYPE_BUS, .instance_size = sizeof(I3CBus), .class_size = sizeof(I3CBusClass), + .class_init = i3c_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_HOTPLUG_HANDLER }, + { } + } }; I3CBus *i3c_init_bus(DeviceState *parent, const char *name)