From e5f15001448a3cc2cf1b2181ea02cd7b4e03fc56 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Fri, 29 Dec 2023 22:24:14 +1000 Subject: tests/avocado: mark boot_linux.py long runtime instead of flaky MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ppc64 and s390x tests were first marked skipIf GITLAB_CI by commit c0c8687ef0f ("tests/avocado: disable BootLinuxPPC64 test in CI"), and commit 0f26d94ec9e ("tests/acceptance: skip s390x_ccw_vrtio_tcg on GitLab") due to being very heavy-weight for gitlab CI. Commit 9b45cc99318 ("docs/devel: rationalise unstable gitlab tests under FLAKY_TESTS") changed this to being flaky but it isn't really, it just had a long runtime. So take the SPEED=slow variable from qtests and introduce it to avocado, and make these tests require it. Reviewed-by: Cédric Le Goater Signed-off-by: Nicholas Piggin --- tests/avocado/boot_linux.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'tests') diff --git a/tests/avocado/boot_linux.py b/tests/avocado/boot_linux.py index 7c4769904e..de4c8805f7 100644 --- a/tests/avocado/boot_linux.py +++ b/tests/avocado/boot_linux.py @@ -93,13 +93,11 @@ class BootLinuxPPC64(LinuxTest): timeout = 360 - @skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test is unstable on GitLab') - + @skipUnless(os.getenv('SPEED') == 'slow', 'runtime limited') def test_pseries_tcg(self): """ :avocado: tags=machine:pseries :avocado: tags=accel:tcg - :avocado: tags=flaky """ self.require_accelerator("tcg") self.vm.add_args("-accel", "tcg") @@ -113,13 +111,11 @@ class BootLinuxS390X(LinuxTest): timeout = 240 - @skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test is unstable on GitLab') - + @skipUnless(os.getenv('SPEED') == 'slow', 'runtime limited') def test_s390_ccw_virtio_tcg(self): """ :avocado: tags=machine:s390-ccw-virtio :avocado: tags=accel:tcg - :avocado: tags=flaky """ self.require_accelerator("tcg") self.vm.add_args("-accel", "tcg") -- cgit 1.4.1 From 458a6aa3b3346c33a9be9441f2f17f2937e88b72 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Wed, 24 Jan 2024 15:49:10 +1000 Subject: tests/avocado: improve flaky ppc/pnv boot_linux_console.py test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The expected MTD partition detection output does not always appear on the console, despite the test reaching the boot loader and the string appearing in dmesg. Possibly due to an init script that quietens the console output. Using an earlier log message improves reliability. Reviewed-by: Cédric Le Goater Signed-off-by: Nicholas Piggin --- tests/avocado/boot_linux_console.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'tests') diff --git a/tests/avocado/boot_linux_console.py b/tests/avocado/boot_linux_console.py index 3f0180e1f8..af104fff1c 100644 --- a/tests/avocado/boot_linux_console.py +++ b/tests/avocado/boot_linux_console.py @@ -1368,7 +1368,8 @@ class BootLinuxConsole(LinuxKernelTest): self.wait_for_console_pattern("CPU: " + proc + " generation processor") self.wait_for_console_pattern("zImage starting: loaded") self.wait_for_console_pattern("Run /init as init process") - self.wait_for_console_pattern("Creating 1 MTD partitions") + # Device detection output driven by udev probing is sometimes cut off + # from console output, suspect S14silence-console init script. def test_ppc_powernv8(self): """ -- cgit 1.4.1 From 234aa6d62a2ad5790efcaa2a99c1adf6f5744350 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Tue, 12 Sep 2023 08:31:42 +1000 Subject: tests/avocado: ppc add powernv10 boot_linux_console test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add test for POWER10. Reviewed-by: Cédric Le Goater Signed-off-by: Nicholas Piggin --- tests/avocado/boot_linux_console.py | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'tests') diff --git a/tests/avocado/boot_linux_console.py b/tests/avocado/boot_linux_console.py index af104fff1c..a00202df3c 100644 --- a/tests/avocado/boot_linux_console.py +++ b/tests/avocado/boot_linux_console.py @@ -1387,6 +1387,14 @@ class BootLinuxConsole(LinuxKernelTest): """ self.do_test_ppc64_powernv('P9') + def test_ppc_powernv10(self): + """ + :avocado: tags=arch:ppc64 + :avocado: tags=machine:powernv10 + :avocado: tags=accel:tcg + """ + self.do_test_ppc64_powernv('P10') + def test_ppc_g3beige(self): """ :avocado: tags=arch:ppc -- cgit 1.4.1 From 8d07a8aef6feb7d8db4fabdcc3753636c562febe Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Thu, 5 Oct 2023 19:28:08 +1000 Subject: tests/avocado: Add ppc pseries and powernv hash MMU tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit POWER CPUs support hash and radix MMU modes. Linux supports running in either mode, but defaults to radix. To keep up testing of QEMU's hash MMU implementation, add some Linux hash boot tests. Reviewed-by: Cédric Le Goater Signed-off-by: Nicholas Piggin --- tests/avocado/ppc_powernv.py | 23 +++++++++++++++++++---- tests/avocado/ppc_pseries.py | 20 +++++++++++++++++--- 2 files changed, 36 insertions(+), 7 deletions(-) (limited to 'tests') diff --git a/tests/avocado/ppc_powernv.py b/tests/avocado/ppc_powernv.py index d0e5c07bde..4342941d5d 100644 --- a/tests/avocado/ppc_powernv.py +++ b/tests/avocado/ppc_powernv.py @@ -12,11 +12,11 @@ from avocado_qemu import wait_for_console_pattern class powernvMachine(QemuSystemTest): timeout = 90 - KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 ' + KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 console=hvc0 ' panic_message = 'Kernel panic - not syncing' good_message = 'VFS: Cannot open root device' - def do_test_linux_boot(self): + def do_test_linux_boot(self, command_line = KERNEL_COMMON_COMMAND_LINE): self.require_accelerator("tcg") kernel_url = ('https://archives.fedoraproject.org/pub/archive' '/fedora-secondary/releases/29/Everything/ppc64le/os' @@ -25,9 +25,8 @@ class powernvMachine(QemuSystemTest): kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) self.vm.set_console() - kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=hvc0' self.vm.add_args('-kernel', kernel_path, - '-append', kernel_command_line) + '-append', command_line) self.vm.launch() def test_linux_boot(self): @@ -54,6 +53,22 @@ class powernvMachine(QemuSystemTest): wait_for_console_pattern(self, console_pattern, self.panic_message) wait_for_console_pattern(self, self.good_message, self.panic_message) + def test_linux_smp_hpt_boot(self): + """ + :avocado: tags=arch:ppc64 + :avocado: tags=machine:powernv + :avocado: tags=accel:tcg + """ + + self.vm.add_args('-smp', '4') + self.do_test_linux_boot(self.KERNEL_COMMON_COMMAND_LINE + + 'disable_radix') + console_pattern = 'smp: Brought up 1 node, 4 CPUs' + wait_for_console_pattern(self, 'hash-mmu: Initializing hash mmu', + self.panic_message) + wait_for_console_pattern(self, console_pattern, self.panic_message) + wait_for_console_pattern(self, self.good_message, self.panic_message) + def test_linux_smt_boot(self): """ :avocado: tags=arch:ppc64 diff --git a/tests/avocado/ppc_pseries.py b/tests/avocado/ppc_pseries.py index a8311e6555..74aaa4ac4a 100644 --- a/tests/avocado/ppc_pseries.py +++ b/tests/avocado/ppc_pseries.py @@ -12,11 +12,11 @@ from avocado_qemu import wait_for_console_pattern class pseriesMachine(QemuSystemTest): timeout = 90 - KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 ' + KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 console=hvc0 ' panic_message = 'Kernel panic - not syncing' good_message = 'VFS: Cannot open root device' - def do_test_ppc64_linux_boot(self): + def do_test_ppc64_linux_boot(self, kernel_command_line = KERNEL_COMMON_COMMAND_LINE): kernel_url = ('https://archives.fedoraproject.org/pub/archive' '/fedora-secondary/releases/29/Everything/ppc64le/os' '/ppc/ppc64/vmlinuz') @@ -24,7 +24,6 @@ class pseriesMachine(QemuSystemTest): kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) self.vm.set_console() - kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=hvc0' self.vm.add_args('-kernel', kernel_path, '-append', kernel_command_line) self.vm.launch() @@ -62,6 +61,21 @@ class pseriesMachine(QemuSystemTest): wait_for_console_pattern(self, console_pattern, self.panic_message) wait_for_console_pattern(self, self.good_message, self.panic_message) + def test_ppc64_linux_hpt_smp_boot(self): + """ + :avocado: tags=arch:ppc64 + :avocado: tags=machine:pseries + """ + + self.vm.add_args('-smp', '4') + self.do_test_ppc64_linux_boot(self.KERNEL_COMMON_COMMAND_LINE + + 'disable_radix') + console_pattern = 'smp: Brought up 1 node, 4 CPUs' + wait_for_console_pattern(self, 'hash-mmu: Initializing hash mmu', + self.panic_message) + wait_for_console_pattern(self, console_pattern, self.panic_message) + wait_for_console_pattern(self, self.good_message, self.panic_message) + def test_ppc64_linux_smt_boot(self): """ :avocado: tags=arch:ppc64 -- cgit 1.4.1 From 9bf9479328ab3c4a15ef0beb2c93b5e82b8f5209 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Tue, 3 Oct 2023 10:42:45 +1000 Subject: tests/avocado: Add pseries KVM boot_linux test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ppc has no avocado tests for the KVM backend. Add a KVM boot_linux.py test for pseries. Reviewed-by: Cédric Le Goater Signed-off-by: Nicholas Piggin --- tests/avocado/boot_linux.py | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'tests') diff --git a/tests/avocado/boot_linux.py b/tests/avocado/boot_linux.py index de4c8805f7..cdce4cbcba 100644 --- a/tests/avocado/boot_linux.py +++ b/tests/avocado/boot_linux.py @@ -103,6 +103,15 @@ class BootLinuxPPC64(LinuxTest): self.vm.add_args("-accel", "tcg") self.launch_and_wait(set_up_ssh_connection=False) + def test_pseries_kvm(self): + """ + :avocado: tags=machine:pseries + :avocado: tags=accel:kvm + """ + self.require_accelerator("kvm") + self.vm.add_args("-accel", "kvm") + self.vm.add_args("-machine", "cap-ccf-assist=off") + self.launch_and_wait(set_up_ssh_connection=False) class BootLinuxS390X(LinuxTest): """ -- cgit 1.4.1 From c9cb496710758a5d4e05d11a1b0fba1c49c67d34 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 9 Oct 2023 13:25:50 +1000 Subject: tests/avocado: ppc add hypervisor tests The powernv and pseries machines both provide hypervisor facilities that are supported by KVM. This is a large and complicated set of features that don't get much system-level testing in ppc tests. Add a new test case for these which runs QEMU KVM inside the target. This downloads an Alpine VM image, boots it and downloads and installs the qemu package, then boots a virtual machine under it, re-using the original Alpine VM image. Signed-off-by: Nicholas Piggin --- MAINTAINERS | 1 + tests/avocado/ppc_hv_tests.py | 202 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 203 insertions(+) create mode 100644 tests/avocado/ppc_hv_tests.py (limited to 'tests') diff --git a/MAINTAINERS b/MAINTAINERS index 9dd98a923f..3b409b42a5 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1526,6 +1526,7 @@ F: tests/qtest/libqos/*spapr* F: tests/qtest/rtas* F: tests/qtest/libqos/rtas* F: tests/avocado/ppc_pseries.py +F: tests/avocado/ppc_hv_tests.py PowerNV (Non-Virtualized) M: Cédric Le Goater diff --git a/tests/avocado/ppc_hv_tests.py b/tests/avocado/ppc_hv_tests.py new file mode 100644 index 0000000000..5080358e25 --- /dev/null +++ b/tests/avocado/ppc_hv_tests.py @@ -0,0 +1,202 @@ +# Tests that specifically try to exercise hypervisor features of the +# target machines. powernv supports the Power hypervisor ISA, and +# pseries supports the nested-HV hypervisor spec. +# +# Copyright (c) 2023 IBM Corporation +# +# This work is licensed under the terms of the GNU GPL, version 2 or +# later. See the COPYING file in the top-level directory. + +from avocado import skipIf, skipUnless +from avocado.utils import archive +from avocado_qemu import QemuSystemTest +from avocado_qemu import wait_for_console_pattern, exec_command +import os +import time +import subprocess + +deps = ["xorriso"] # dependent tools needed in the test setup/box. + +def which(tool): + """ looks up the full path for @tool, returns None if not found + or if @tool does not have executable permissions. + """ + paths=os.getenv('PATH') + for p in paths.split(os.path.pathsep): + p = os.path.join(p, tool) + if os.path.exists(p) and os.access(p, os.X_OK): + return p + return None + +def missing_deps(): + """ returns True if any of the test dependent tools are absent. + """ + for dep in deps: + if which(dep) is None: + return True + return False + +# Alpine is a light weight distro that supports QEMU. These tests boot +# that on the machine then run a QEMU guest inside it in KVM mode, +# that runs the same Alpine distro image. +# QEMU packages are downloaded and installed on each test. That's not a +# large download, but it may be more polite to create qcow2 image with +# QEMU already installed and use that. +@skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test sometimes gets stuck due to console handling problem') +@skipUnless(os.getenv('AVOCADO_ALLOW_LARGE_STORAGE'), 'storage limited') +@skipUnless(os.getenv('SPEED') == 'slow', 'runtime limited') +@skipIf(missing_deps(), 'dependencies (%s) not installed' % ','.join(deps)) +class HypervisorTest(QemuSystemTest): + + timeout = 1000 + KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 console=hvc0 ' + panic_message = 'Kernel panic - not syncing' + good_message = 'VFS: Cannot open root device' + + def extract_from_iso(self, iso, path): + """ + Extracts a file from an iso file into the test workdir + + :param iso: path to the iso file + :param path: path within the iso file of the file to be extracted + :returns: path of the extracted file + """ + filename = os.path.basename(path) + + cwd = os.getcwd() + os.chdir(self.workdir) + + with open(filename, "w") as outfile: + cmd = "xorriso -osirrox on -indev %s -cpx %s %s" % (iso, path, filename) + subprocess.run(cmd.split(), + stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + + os.chdir(cwd) + + # Return complete path to extracted file. Because callers to + # extract_from_iso() specify 'path' with a leading slash, it is + # necessary to use os.path.relpath() as otherwise os.path.join() + # interprets it as an absolute path and drops the self.workdir part. + return os.path.normpath(os.path.join(self.workdir, filename)) + + def setUp(self): + super().setUp() + + iso_url = ('https://dl-cdn.alpinelinux.org/alpine/v3.18/releases/ppc64le/alpine-standard-3.18.4-ppc64le.iso') + + # Alpine use sha256 so I recalculated this myself + iso_sha256 = 'c26b8d3e17c2f3f0fed02b4b1296589c2390e6d5548610099af75300edd7b3ff' + iso_path = self.fetch_asset(iso_url, asset_hash=iso_sha256, + algorithm = "sha256") + + self.iso_path = iso_path + self.vmlinuz = self.extract_from_iso(iso_path, '/boot/vmlinuz-lts') + self.initramfs = self.extract_from_iso(iso_path, '/boot/initramfs-lts') + + def do_start_alpine(self): + self.vm.set_console() + kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + self.vm.add_args("-kernel", self.vmlinuz) + self.vm.add_args("-initrd", self.initramfs) + self.vm.add_args("-smp", "4", "-m", "2g") + self.vm.add_args("-drive", f"file={self.iso_path},format=raw,if=none,id=drive0") + + self.vm.launch() + wait_for_console_pattern(self, 'Welcome to Alpine Linux 3.18') + exec_command(self, 'root') + wait_for_console_pattern(self, 'localhost login:') + wait_for_console_pattern(self, 'You may change this message by editing /etc/motd.') + exec_command(self, 'setup-alpine -qe') + wait_for_console_pattern(self, 'Updating repository indexes... done.') + + def do_stop_alpine(self): + exec_command(self, 'poweroff') + wait_for_console_pattern(self, 'alpine:~#') + self.vm.wait() + + def do_setup_kvm(self): + exec_command(self, 'echo http://dl-cdn.alpinelinux.org/alpine/v3.18/main > /etc/apk/repositories') + wait_for_console_pattern(self, 'alpine:~#') + exec_command(self, 'echo http://dl-cdn.alpinelinux.org/alpine/v3.18/community >> /etc/apk/repositories') + wait_for_console_pattern(self, 'alpine:~#') + exec_command(self, 'apk update') + wait_for_console_pattern(self, 'alpine:~#') + exec_command(self, 'apk add qemu-system-ppc64') + wait_for_console_pattern(self, 'alpine:~#') + exec_command(self, 'modprobe kvm-hv') + wait_for_console_pattern(self, 'alpine:~#') + + # This uses the host's block device as the source file for guest block + # device for install media. This is a bit hacky but allows reuse of the + # iso without having a passthrough filesystem configured. + def do_test_kvm(self, hpt=False): + if hpt: + append = 'disable_radix' + else: + append = '' + exec_command(self, 'qemu-system-ppc64 -nographic -smp 2 -m 1g ' + '-machine pseries,x-vof=on,accel=kvm ' + '-machine cap-cfpc=broken,cap-sbbc=broken,' + 'cap-ibs=broken,cap-ccf-assist=off ' + '-drive file=/dev/nvme0n1,format=raw,readonly=on ' + '-initrd /media/nvme0n1/boot/initramfs-lts ' + '-kernel /media/nvme0n1/boot/vmlinuz-lts ' + '-append \'usbcore.nousb ' + append + '\'') + # Alpine 3.18 kernel seems to crash in XHCI USB driver. + wait_for_console_pattern(self, 'Welcome to Alpine Linux 3.18') + exec_command(self, 'root') + wait_for_console_pattern(self, 'localhost login:') + wait_for_console_pattern(self, 'You may change this message by editing /etc/motd.') + exec_command(self, 'poweroff >& /dev/null') + wait_for_console_pattern(self, 'localhost:~#') + wait_for_console_pattern(self, 'reboot: Power down') + time.sleep(1) + exec_command(self, '') + wait_for_console_pattern(self, 'alpine:~#') + + def test_hv_pseries(self): + """ + :avocado: tags=arch:ppc64 + :avocado: tags=machine:pseries + :avocado: tags=accel:tcg + """ + self.require_accelerator("tcg") + self.vm.add_args("-accel", "tcg,thread=multi") + self.vm.add_args('-device', 'nvme,serial=1234,drive=drive0') + self.vm.add_args("-machine", "x-vof=on,cap-nested-hv=on") + self.do_start_alpine() + self.do_setup_kvm() + self.do_test_kvm() + self.do_stop_alpine() + + def test_hv_pseries_kvm(self): + """ + :avocado: tags=arch:ppc64 + :avocado: tags=machine:pseries + :avocado: tags=accel:kvm + """ + self.require_accelerator("kvm") + self.vm.add_args("-accel", "kvm") + self.vm.add_args('-device', 'nvme,serial=1234,drive=drive0') + self.vm.add_args("-machine", "x-vof=on,cap-nested-hv=on,cap-ccf-assist=off") + self.do_start_alpine() + self.do_setup_kvm() + self.do_test_kvm() + self.do_stop_alpine() + + def test_hv_powernv(self): + """ + :avocado: tags=arch:ppc64 + :avocado: tags=machine:powernv + :avocado: tags=accel:tcg + """ + self.require_accelerator("tcg") + self.vm.add_args("-accel", "tcg,thread=multi") + self.vm.add_args('-device', 'nvme,bus=pcie.2,addr=0x0,serial=1234,drive=drive0', + '-device', 'e1000e,netdev=net0,mac=C0:FF:EE:00:00:02,bus=pcie.0,addr=0x0', + '-netdev', 'user,id=net0,hostfwd=::20022-:22,hostname=alpine') + self.do_start_alpine() + self.do_setup_kvm() + self.do_test_kvm() + self.do_test_kvm(True) + self.do_stop_alpine() -- cgit 1.4.1 From b41484ccb695fd104776fe9eb964a6360ef2534a Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Thu, 5 Oct 2023 18:27:28 +1000 Subject: tests/avocado: Use default CPU for pseries machine Use the default CPU with the pseries machine unless there is a specific requirement. Signed-off-by: Nicholas Piggin --- tests/avocado/migration.py | 1 - 1 file changed, 1 deletion(-) (limited to 'tests') diff --git a/tests/avocado/migration.py b/tests/avocado/migration.py index 09b62f813e..be6234b3c2 100644 --- a/tests/avocado/migration.py +++ b/tests/avocado/migration.py @@ -123,7 +123,6 @@ class PPC64(MigrationTest): """ :avocado: tags=arch:ppc64 :avocado: tags=machine:pseries - :avocado: tags=cpu:power9_v2.0 """ def test_migration_with_tcp_localhost(self): -- cgit 1.4.1 From 7b99fb30b39bb14733aa3645b68e0103bbf8e43a Mon Sep 17 00:00:00 2001 From: Glenn Miles Date: Thu, 25 Jan 2024 16:48:10 -0600 Subject: misc/pca9552: Fix inverted input status The pca9552 INPUT0 and INPUT1 registers are supposed to hold the logical values of the LED pins. A logical 0 should be seen in the INPUT0/1 registers for a pin when its corresponding LSn bits are set to 0, which is also the state needed for turning on an LED in a typical usage scenario. Existing code was doing the opposite and setting INPUT0/1 bit to a 1 when the LSn bit was set to 0, so this commit fixes that. Reviewed-by: Andrew Jeffery Signed-off-by: Glenn Miles Signed-off-by: Nicholas Piggin --- hw/misc/pca9552.c | 18 +++++++++++++----- tests/qtest/pca9552-test.c | 6 +++--- 2 files changed, 16 insertions(+), 8 deletions(-) (limited to 'tests') diff --git a/hw/misc/pca9552.c b/hw/misc/pca9552.c index 72b653463f..f00a149d61 100644 --- a/hw/misc/pca9552.c +++ b/hw/misc/pca9552.c @@ -36,7 +36,10 @@ typedef struct PCA955xClass PCA955xClass; DECLARE_CLASS_CHECKERS(PCA955xClass, PCA955X, TYPE_PCA955X) - +/* + * Note: The LED_ON and LED_OFF configuration values for the PCA955X + * chips are the reverse of the PCA953X family of chips. + */ #define PCA9552_LED_ON 0x0 #define PCA9552_LED_OFF 0x1 #define PCA9552_LED_PWM0 0x2 @@ -112,13 +115,18 @@ static void pca955x_update_pin_input(PCA955xState *s) switch (config) { case PCA9552_LED_ON: - qemu_set_irq(s->gpio[i], 1); - s->regs[input_reg] |= 1 << input_shift; - break; - case PCA9552_LED_OFF: + /* Pin is set to 0V to turn on LED */ qemu_set_irq(s->gpio[i], 0); s->regs[input_reg] &= ~(1 << input_shift); break; + case PCA9552_LED_OFF: + /* + * Pin is set to Hi-Z to turn off LED and + * pullup sets it to a logical 1. + */ + qemu_set_irq(s->gpio[i], 1); + s->regs[input_reg] |= 1 << input_shift; + break; case PCA9552_LED_PWM0: case PCA9552_LED_PWM1: /* TODO */ diff --git a/tests/qtest/pca9552-test.c b/tests/qtest/pca9552-test.c index d80ed93cd3..ccca2b3d91 100644 --- a/tests/qtest/pca9552-test.c +++ b/tests/qtest/pca9552-test.c @@ -60,7 +60,7 @@ static void send_and_receive(void *obj, void *data, QGuestAllocator *alloc) g_assert_cmphex(value, ==, 0x55); value = i2c_get8(i2cdev, PCA9552_INPUT0); - g_assert_cmphex(value, ==, 0x0); + g_assert_cmphex(value, ==, 0xFF); pca9552_init(i2cdev); @@ -68,13 +68,13 @@ static void send_and_receive(void *obj, void *data, QGuestAllocator *alloc) g_assert_cmphex(value, ==, 0x54); value = i2c_get8(i2cdev, PCA9552_INPUT0); - g_assert_cmphex(value, ==, 0x01); + g_assert_cmphex(value, ==, 0xFE); value = i2c_get8(i2cdev, PCA9552_LS3); g_assert_cmphex(value, ==, 0x54); value = i2c_get8(i2cdev, PCA9552_INPUT1); - g_assert_cmphex(value, ==, 0x10); + g_assert_cmphex(value, ==, 0xEF); } static void pca9552_register_nodes(void) -- cgit 1.4.1 From 4d2cd2d8697164927620fe31f46f4a67e86c4f5f Mon Sep 17 00:00:00 2001 From: Glenn Miles Date: Mon, 5 Feb 2024 17:40:17 +1000 Subject: ppc/pnv: Test pnv i2c master and connected devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tests the following for both P9 and P10: - I2C master POR status - I2C master status after immediate reset Tests the following for powernv10-ranier only: - Config pca9552 hotplug device pins as inputs then Read the INPUT0/1 registers to verify all pins are high - Connected GPIO pin tests of P10 PCA9552 device. Tests output of pins 0-4 affect input of pins 5-9 respectively. - PCA9554 GPIO pins test. Tests input and ouput functionality. Reviewed-by: Cédric Le Goater Signed-off-by: Glenn Miles Signed-off-by: Nicholas Piggin --- hw/ppc/pnv_i2c.c | 131 +---------- include/hw/i2c/pnv_i2c_regs.h | 143 ++++++++++++ tests/qtest/meson.build | 1 + tests/qtest/pnv-host-i2c-test.c | 491 ++++++++++++++++++++++++++++++++++++++++ tests/qtest/pnv-xscom-test.c | 61 +---- tests/qtest/pnv-xscom.h | 80 +++++++ 6 files changed, 717 insertions(+), 190 deletions(-) create mode 100644 include/hw/i2c/pnv_i2c_regs.h create mode 100644 tests/qtest/pnv-host-i2c-test.c create mode 100644 tests/qtest/pnv-xscom.h (limited to 'tests') diff --git a/hw/ppc/pnv_i2c.c b/hw/ppc/pnv_i2c.c index 774946d6b2..4581cc5e5d 100644 --- a/hw/ppc/pnv_i2c.c +++ b/hw/ppc/pnv_i2c.c @@ -22,136 +22,7 @@ #include -/* I2C FIFO register */ -#define I2C_FIFO_REG 0x4 -#define I2C_FIFO PPC_BITMASK(0, 7) - -/* I2C command register */ -#define I2C_CMD_REG 0x5 -#define I2C_CMD_WITH_START PPC_BIT(0) -#define I2C_CMD_WITH_ADDR PPC_BIT(1) -#define I2C_CMD_READ_CONT PPC_BIT(2) -#define I2C_CMD_WITH_STOP PPC_BIT(3) -#define I2C_CMD_INTR_STEERING PPC_BITMASK(6, 7) /* P9 */ -#define I2C_CMD_INTR_STEER_HOST 1 -#define I2C_CMD_INTR_STEER_OCC 2 -#define I2C_CMD_DEV_ADDR PPC_BITMASK(8, 14) -#define I2C_CMD_READ_NOT_WRITE PPC_BIT(15) -#define I2C_CMD_LEN_BYTES PPC_BITMASK(16, 31) -#define I2C_MAX_TFR_LEN 0xfff0ull - -/* I2C mode register */ -#define I2C_MODE_REG 0x6 -#define I2C_MODE_BIT_RATE_DIV PPC_BITMASK(0, 15) -#define I2C_MODE_PORT_NUM PPC_BITMASK(16, 21) -#define I2C_MODE_ENHANCED PPC_BIT(28) -#define I2C_MODE_DIAGNOSTIC PPC_BIT(29) -#define I2C_MODE_PACING_ALLOW PPC_BIT(30) -#define I2C_MODE_WRAP PPC_BIT(31) - -/* I2C watermark register */ -#define I2C_WATERMARK_REG 0x7 -#define I2C_WATERMARK_HIGH PPC_BITMASK(16, 19) -#define I2C_WATERMARK_LOW PPC_BITMASK(24, 27) - -/* - * I2C interrupt mask and condition registers - * - * NB: The function of 0x9 and 0xa changes depending on whether you're reading - * or writing to them. When read they return the interrupt condition bits - * and on writes they update the interrupt mask register. - * - * The bit definitions are the same for all the interrupt registers. - */ -#define I2C_INTR_MASK_REG 0x8 - -#define I2C_INTR_RAW_COND_REG 0x9 /* read */ -#define I2C_INTR_MASK_OR_REG 0x9 /* write*/ - -#define I2C_INTR_COND_REG 0xa /* read */ -#define I2C_INTR_MASK_AND_REG 0xa /* write */ - -#define I2C_INTR_ALL PPC_BITMASK(16, 31) -#define I2C_INTR_INVALID_CMD PPC_BIT(16) -#define I2C_INTR_LBUS_PARITY_ERR PPC_BIT(17) -#define I2C_INTR_BKEND_OVERRUN_ERR PPC_BIT(18) -#define I2C_INTR_BKEND_ACCESS_ERR PPC_BIT(19) -#define I2C_INTR_ARBT_LOST_ERR PPC_BIT(20) -#define I2C_INTR_NACK_RCVD_ERR PPC_BIT(21) -#define I2C_INTR_DATA_REQ PPC_BIT(22) -#define I2C_INTR_CMD_COMP PPC_BIT(23) -#define I2C_INTR_STOP_ERR PPC_BIT(24) -#define I2C_INTR_I2C_BUSY PPC_BIT(25) -#define I2C_INTR_NOT_I2C_BUSY PPC_BIT(26) -#define I2C_INTR_SCL_EQ_1 PPC_BIT(28) -#define I2C_INTR_SCL_EQ_0 PPC_BIT(29) -#define I2C_INTR_SDA_EQ_1 PPC_BIT(30) -#define I2C_INTR_SDA_EQ_0 PPC_BIT(31) - -/* I2C status register */ -#define I2C_RESET_I2C_REG 0xb /* write */ -#define I2C_RESET_ERRORS 0xc -#define I2C_STAT_REG 0xb /* read */ -#define I2C_STAT_INVALID_CMD PPC_BIT(0) -#define I2C_STAT_LBUS_PARITY_ERR PPC_BIT(1) -#define I2C_STAT_BKEND_OVERRUN_ERR PPC_BIT(2) -#define I2C_STAT_BKEND_ACCESS_ERR PPC_BIT(3) -#define I2C_STAT_ARBT_LOST_ERR PPC_BIT(4) -#define I2C_STAT_NACK_RCVD_ERR PPC_BIT(5) -#define I2C_STAT_DATA_REQ PPC_BIT(6) -#define I2C_STAT_CMD_COMP PPC_BIT(7) -#define I2C_STAT_STOP_ERR PPC_BIT(8) -#define I2C_STAT_UPPER_THRS PPC_BITMASK(9, 15) -#define I2C_STAT_ANY_I2C_INTR PPC_BIT(16) -#define I2C_STAT_PORT_HISTORY_BUSY PPC_BIT(19) -#define I2C_STAT_SCL_INPUT_LEVEL PPC_BIT(20) -#define I2C_STAT_SDA_INPUT_LEVEL PPC_BIT(21) -#define I2C_STAT_PORT_BUSY PPC_BIT(22) -#define I2C_STAT_INTERFACE_BUSY PPC_BIT(23) -#define I2C_STAT_FIFO_ENTRY_COUNT PPC_BITMASK(24, 31) - -#define I2C_STAT_ANY_ERR (I2C_STAT_INVALID_CMD | I2C_STAT_LBUS_PARITY_ERR | \ - I2C_STAT_BKEND_OVERRUN_ERR | \ - I2C_STAT_BKEND_ACCESS_ERR | I2C_STAT_ARBT_LOST_ERR | \ - I2C_STAT_NACK_RCVD_ERR | I2C_STAT_STOP_ERR) - - -#define I2C_INTR_ACTIVE \ - ((I2C_STAT_ANY_ERR >> 16) | I2C_INTR_CMD_COMP | I2C_INTR_DATA_REQ) - -/* Pseudo-status used for timeouts */ -#define I2C_STAT_PSEUDO_TIMEOUT PPC_BIT(63) - -/* I2C extended status register */ -#define I2C_EXTD_STAT_REG 0xc -#define I2C_EXTD_STAT_FIFO_SIZE PPC_BITMASK(0, 7) -#define I2C_EXTD_STAT_MSM_CURSTATE PPC_BITMASK(11, 15) -#define I2C_EXTD_STAT_SCL_IN_SYNC PPC_BIT(16) -#define I2C_EXTD_STAT_SDA_IN_SYNC PPC_BIT(17) -#define I2C_EXTD_STAT_S_SCL PPC_BIT(18) -#define I2C_EXTD_STAT_S_SDA PPC_BIT(19) -#define I2C_EXTD_STAT_M_SCL PPC_BIT(20) -#define I2C_EXTD_STAT_M_SDA PPC_BIT(21) -#define I2C_EXTD_STAT_HIGH_WATER PPC_BIT(22) -#define I2C_EXTD_STAT_LOW_WATER PPC_BIT(23) -#define I2C_EXTD_STAT_I2C_BUSY PPC_BIT(24) -#define I2C_EXTD_STAT_SELF_BUSY PPC_BIT(25) -#define I2C_EXTD_STAT_I2C_VERSION PPC_BITMASK(27, 31) - -/* I2C residual front end/back end length */ -#define I2C_RESIDUAL_LEN_REG 0xd -#define I2C_RESIDUAL_FRONT_END PPC_BITMASK(0, 15) -#define I2C_RESIDUAL_BACK_END PPC_BITMASK(16, 31) - -/* Port busy register */ -#define I2C_PORT_BUSY_REG 0xe -#define I2C_SET_S_SCL_REG 0xd -#define I2C_RESET_S_SCL_REG 0xf -#define I2C_SET_S_SDA_REG 0x10 -#define I2C_RESET_S_SDA_REG 0x11 - -#define PNV_I2C_FIFO_SIZE 8 -#define PNV_I2C_MAX_BUSSES 64 +#include "hw/i2c/pnv_i2c_regs.h" static I2CBus *pnv_i2c_get_bus(PnvI2C *i2c) { diff --git a/include/hw/i2c/pnv_i2c_regs.h b/include/hw/i2c/pnv_i2c_regs.h new file mode 100644 index 0000000000..85e96ff480 --- /dev/null +++ b/include/hw/i2c/pnv_i2c_regs.h @@ -0,0 +1,143 @@ +/* + * PowerNV I2C Controller Register Definitions + * + * Copyright (c) 2024, IBM Corporation. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef PNV_I2C_REGS_H +#define PNV_I2C_REGS_H + +/* I2C FIFO register */ +#define I2C_FIFO_REG 0x4 +#define I2C_FIFO PPC_BITMASK(0, 7) + +/* I2C command register */ +#define I2C_CMD_REG 0x5 +#define I2C_CMD_WITH_START PPC_BIT(0) +#define I2C_CMD_WITH_ADDR PPC_BIT(1) +#define I2C_CMD_READ_CONT PPC_BIT(2) +#define I2C_CMD_WITH_STOP PPC_BIT(3) +#define I2C_CMD_INTR_STEERING PPC_BITMASK(6, 7) /* P9 */ +#define I2C_CMD_INTR_STEER_HOST 1 +#define I2C_CMD_INTR_STEER_OCC 2 +#define I2C_CMD_DEV_ADDR PPC_BITMASK(8, 14) +#define I2C_CMD_READ_NOT_WRITE PPC_BIT(15) +#define I2C_CMD_LEN_BYTES PPC_BITMASK(16, 31) +#define I2C_MAX_TFR_LEN 0xfff0ull + +/* I2C mode register */ +#define I2C_MODE_REG 0x6 +#define I2C_MODE_BIT_RATE_DIV PPC_BITMASK(0, 15) +#define I2C_MODE_PORT_NUM PPC_BITMASK(16, 21) +#define I2C_MODE_ENHANCED PPC_BIT(28) +#define I2C_MODE_DIAGNOSTIC PPC_BIT(29) +#define I2C_MODE_PACING_ALLOW PPC_BIT(30) +#define I2C_MODE_WRAP PPC_BIT(31) + +/* I2C watermark register */ +#define I2C_WATERMARK_REG 0x7 +#define I2C_WATERMARK_HIGH PPC_BITMASK(16, 19) +#define I2C_WATERMARK_LOW PPC_BITMASK(24, 27) + +/* + * I2C interrupt mask and condition registers + * + * NB: The function of 0x9 and 0xa changes depending on whether you're reading + * or writing to them. When read they return the interrupt condition bits + * and on writes they update the interrupt mask register. + * + * The bit definitions are the same for all the interrupt registers. + */ +#define I2C_INTR_MASK_REG 0x8 + +#define I2C_INTR_RAW_COND_REG 0x9 /* read */ +#define I2C_INTR_MASK_OR_REG 0x9 /* write*/ + +#define I2C_INTR_COND_REG 0xa /* read */ +#define I2C_INTR_MASK_AND_REG 0xa /* write */ + +#define I2C_INTR_ALL PPC_BITMASK(16, 31) +#define I2C_INTR_INVALID_CMD PPC_BIT(16) +#define I2C_INTR_LBUS_PARITY_ERR PPC_BIT(17) +#define I2C_INTR_BKEND_OVERRUN_ERR PPC_BIT(18) +#define I2C_INTR_BKEND_ACCESS_ERR PPC_BIT(19) +#define I2C_INTR_ARBT_LOST_ERR PPC_BIT(20) +#define I2C_INTR_NACK_RCVD_ERR PPC_BIT(21) +#define I2C_INTR_DATA_REQ PPC_BIT(22) +#define I2C_INTR_CMD_COMP PPC_BIT(23) +#define I2C_INTR_STOP_ERR PPC_BIT(24) +#define I2C_INTR_I2C_BUSY PPC_BIT(25) +#define I2C_INTR_NOT_I2C_BUSY PPC_BIT(26) +#define I2C_INTR_SCL_EQ_1 PPC_BIT(28) +#define I2C_INTR_SCL_EQ_0 PPC_BIT(29) +#define I2C_INTR_SDA_EQ_1 PPC_BIT(30) +#define I2C_INTR_SDA_EQ_0 PPC_BIT(31) + +/* I2C status register */ +#define I2C_RESET_I2C_REG 0xb /* write */ +#define I2C_RESET_ERRORS 0xc +#define I2C_STAT_REG 0xb /* read */ +#define I2C_STAT_INVALID_CMD PPC_BIT(0) +#define I2C_STAT_LBUS_PARITY_ERR PPC_BIT(1) +#define I2C_STAT_BKEND_OVERRUN_ERR PPC_BIT(2) +#define I2C_STAT_BKEND_ACCESS_ERR PPC_BIT(3) +#define I2C_STAT_ARBT_LOST_ERR PPC_BIT(4) +#define I2C_STAT_NACK_RCVD_ERR PPC_BIT(5) +#define I2C_STAT_DATA_REQ PPC_BIT(6) +#define I2C_STAT_CMD_COMP PPC_BIT(7) +#define I2C_STAT_STOP_ERR PPC_BIT(8) +#define I2C_STAT_UPPER_THRS PPC_BITMASK(9, 15) +#define I2C_STAT_ANY_I2C_INTR PPC_BIT(16) +#define I2C_STAT_PORT_HISTORY_BUSY PPC_BIT(19) +#define I2C_STAT_SCL_INPUT_LEVEL PPC_BIT(20) +#define I2C_STAT_SDA_INPUT_LEVEL PPC_BIT(21) +#define I2C_STAT_PORT_BUSY PPC_BIT(22) +#define I2C_STAT_INTERFACE_BUSY PPC_BIT(23) +#define I2C_STAT_FIFO_ENTRY_COUNT PPC_BITMASK(24, 31) + +#define I2C_STAT_ANY_ERR (I2C_STAT_INVALID_CMD | I2C_STAT_LBUS_PARITY_ERR | \ + I2C_STAT_BKEND_OVERRUN_ERR | \ + I2C_STAT_BKEND_ACCESS_ERR | I2C_STAT_ARBT_LOST_ERR | \ + I2C_STAT_NACK_RCVD_ERR | I2C_STAT_STOP_ERR) + + +#define I2C_INTR_ACTIVE \ + ((I2C_STAT_ANY_ERR >> 16) | I2C_INTR_CMD_COMP | I2C_INTR_DATA_REQ) + +/* Pseudo-status used for timeouts */ +#define I2C_STAT_PSEUDO_TIMEOUT PPC_BIT(63) + +/* I2C extended status register */ +#define I2C_EXTD_STAT_REG 0xc +#define I2C_EXTD_STAT_FIFO_SIZE PPC_BITMASK(0, 7) +#define I2C_EXTD_STAT_MSM_CURSTATE PPC_BITMASK(11, 15) +#define I2C_EXTD_STAT_SCL_IN_SYNC PPC_BIT(16) +#define I2C_EXTD_STAT_SDA_IN_SYNC PPC_BIT(17) +#define I2C_EXTD_STAT_S_SCL PPC_BIT(18) +#define I2C_EXTD_STAT_S_SDA PPC_BIT(19) +#define I2C_EXTD_STAT_M_SCL PPC_BIT(20) +#define I2C_EXTD_STAT_M_SDA PPC_BIT(21) +#define I2C_EXTD_STAT_HIGH_WATER PPC_BIT(22) +#define I2C_EXTD_STAT_LOW_WATER PPC_BIT(23) +#define I2C_EXTD_STAT_I2C_BUSY PPC_BIT(24) +#define I2C_EXTD_STAT_SELF_BUSY PPC_BIT(25) +#define I2C_EXTD_STAT_I2C_VERSION PPC_BITMASK(27, 31) + +/* I2C residual front end/back end length */ +#define I2C_RESIDUAL_LEN_REG 0xd +#define I2C_RESIDUAL_FRONT_END PPC_BITMASK(0, 15) +#define I2C_RESIDUAL_BACK_END PPC_BITMASK(16, 31) + +/* Port busy register */ +#define I2C_PORT_BUSY_REG 0xe +#define I2C_SET_S_SCL_REG 0xd +#define I2C_RESET_S_SCL_REG 0xf +#define I2C_SET_S_SDA_REG 0x10 +#define I2C_RESET_S_SDA_REG 0x11 + +#define PNV_I2C_FIFO_SIZE 8 +#define PNV_I2C_MAX_BUSSES 64 + +#endif /* PNV_I2C_REGS_H */ diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index 430d49b409..6ea77893f5 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -167,6 +167,7 @@ qtests_ppc64 = \ qtests_ppc + \ (config_all_devices.has_key('CONFIG_PSERIES') ? ['device-plug-test'] : []) + \ (config_all_devices.has_key('CONFIG_POWERNV') ? ['pnv-xscom-test'] : []) + \ + (config_all_devices.has_key('CONFIG_POWERNV') ? ['pnv-host-i2c-test'] : []) + \ (config_all_devices.has_key('CONFIG_PSERIES') ? ['rtas-test'] : []) + \ (slirp.found() ? ['pxe-test'] : []) + \ (config_all_devices.has_key('CONFIG_USB_UHCI') ? ['usb-hcd-uhci-test'] : []) + \ diff --git a/tests/qtest/pnv-host-i2c-test.c b/tests/qtest/pnv-host-i2c-test.c new file mode 100644 index 0000000000..c635177252 --- /dev/null +++ b/tests/qtest/pnv-host-i2c-test.c @@ -0,0 +1,491 @@ +/* + * QTest testcase for PowerNV 10 Host I2C Communications + * + * Copyright (c) 2023, IBM Corporation. + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * later. See the COPYING file in the top-level directory. + */ +#include "qemu/osdep.h" +#include "libqtest.h" +#include "hw/misc/pca9554_regs.h" +#include "hw/misc/pca9552_regs.h" +#include "pnv-xscom.h" + +#define PPC_BIT(bit) (0x8000000000000000ULL >> (bit)) +#define PPC_BIT32(bit) (0x80000000 >> (bit)) +#define PPC_BIT8(bit) (0x80 >> (bit)) +#define PPC_BITMASK(bs, be) ((PPC_BIT(bs) - PPC_BIT(be)) | PPC_BIT(bs)) +#define PPC_BITMASK32(bs, be) ((PPC_BIT32(bs) - PPC_BIT32(be)) | \ + PPC_BIT32(bs)) + +#define MASK_TO_LSH(m) (__builtin_ffsll(m) - 1) +#define GETFIELD(m, v) (((v) & (m)) >> MASK_TO_LSH(m)) +#define SETFIELD(m, v, val) \ + (((v) & ~(m)) | ((((typeof(v))(val)) << MASK_TO_LSH(m)) & (m))) + +#define PNV10_XSCOM_I2CM_BASE 0xa0000 +#define PNV10_XSCOM_I2CM_SIZE 0x1000 + +#include "hw/i2c/pnv_i2c_regs.h" + +typedef struct { + QTestState *qts; + const PnvChip *chip; + int engine; +} PnvI2cCtlr; + +typedef struct { + PnvI2cCtlr *ctlr; + int port; + uint8_t addr; +} PnvI2cDev; + + +static uint64_t pnv_i2c_xscom_addr(PnvI2cCtlr *ctlr, uint32_t reg) +{ + return pnv_xscom_addr(ctlr->chip, PNV10_XSCOM_I2CM_BASE + + (PNV10_XSCOM_I2CM_SIZE * ctlr->engine) + reg); +} + +static uint64_t pnv_i2c_xscom_read(PnvI2cCtlr *ctlr, uint32_t reg) +{ + return qtest_readq(ctlr->qts, pnv_i2c_xscom_addr(ctlr, reg)); +} + +static void pnv_i2c_xscom_write(PnvI2cCtlr *ctlr, uint32_t reg, uint64_t val) +{ + qtest_writeq(ctlr->qts, pnv_i2c_xscom_addr(ctlr, reg), val); +} + +/* Write len bytes from buf to i2c device with given addr and port */ +static void pnv_i2c_send(PnvI2cDev *dev, const uint8_t *buf, uint16_t len) +{ + int byte_num; + uint64_t reg64; + + /* select requested port */ + reg64 = SETFIELD(I2C_MODE_BIT_RATE_DIV, 0ull, 0x2be); + reg64 = SETFIELD(I2C_MODE_PORT_NUM, reg64, dev->port); + pnv_i2c_xscom_write(dev->ctlr, I2C_MODE_REG, reg64); + + /* check status for cmd complete and bus idle */ + reg64 = pnv_i2c_xscom_read(dev->ctlr, I2C_EXTD_STAT_REG); + g_assert_cmphex(reg64 & I2C_EXTD_STAT_I2C_BUSY, ==, 0); + reg64 = pnv_i2c_xscom_read(dev->ctlr, I2C_STAT_REG); + g_assert_cmphex(reg64 & (I2C_STAT_ANY_ERR | I2C_STAT_CMD_COMP), ==, + I2C_STAT_CMD_COMP); + + /* Send start, with stop, with address and len bytes of data */ + reg64 = I2C_CMD_WITH_START | I2C_CMD_WITH_ADDR | I2C_CMD_WITH_STOP; + reg64 = SETFIELD(I2C_CMD_DEV_ADDR, reg64, dev->addr); + reg64 = SETFIELD(I2C_CMD_LEN_BYTES, reg64, len); + pnv_i2c_xscom_write(dev->ctlr, I2C_CMD_REG, reg64); + + /* check status for errors */ + reg64 = pnv_i2c_xscom_read(dev->ctlr, I2C_STAT_REG); + g_assert_cmphex(reg64 & I2C_STAT_ANY_ERR, ==, 0); + + /* write data bytes to fifo register */ + for (byte_num = 0; byte_num < len; byte_num++) { + reg64 = SETFIELD(I2C_FIFO, 0ull, buf[byte_num]); + pnv_i2c_xscom_write(dev->ctlr, I2C_FIFO_REG, reg64); + } + + /* check status for cmd complete and bus idle */ + reg64 = pnv_i2c_xscom_read(dev->ctlr, I2C_EXTD_STAT_REG); + g_assert_cmphex(reg64 & I2C_EXTD_STAT_I2C_BUSY, ==, 0); + reg64 = pnv_i2c_xscom_read(dev->ctlr, I2C_STAT_REG); + g_assert_cmphex(reg64 & (I2C_STAT_ANY_ERR | I2C_STAT_CMD_COMP), ==, + I2C_STAT_CMD_COMP); +} + +/* Recieve len bytes into buf from i2c device with given addr and port */ +static void pnv_i2c_recv(PnvI2cDev *dev, uint8_t *buf, uint16_t len) +{ + int byte_num; + uint64_t reg64; + + /* select requested port */ + reg64 = SETFIELD(I2C_MODE_BIT_RATE_DIV, 0ull, 0x2be); + reg64 = SETFIELD(I2C_MODE_PORT_NUM, reg64, dev->port); + pnv_i2c_xscom_write(dev->ctlr, I2C_MODE_REG, reg64); + + /* check status for cmd complete and bus idle */ + reg64 = pnv_i2c_xscom_read(dev->ctlr, I2C_EXTD_STAT_REG); + g_assert_cmphex(reg64 & I2C_EXTD_STAT_I2C_BUSY, ==, 0); + reg64 = pnv_i2c_xscom_read(dev->ctlr, I2C_STAT_REG); + g_assert_cmphex(reg64 & (I2C_STAT_ANY_ERR | I2C_STAT_CMD_COMP), ==, + I2C_STAT_CMD_COMP); + + /* Send start, with stop, with address and len bytes of data */ + reg64 = I2C_CMD_WITH_START | I2C_CMD_WITH_ADDR | + I2C_CMD_WITH_STOP | I2C_CMD_READ_NOT_WRITE; + reg64 = SETFIELD(I2C_CMD_DEV_ADDR, reg64, dev->addr); + reg64 = SETFIELD(I2C_CMD_LEN_BYTES, reg64, len); + pnv_i2c_xscom_write(dev->ctlr, I2C_CMD_REG, reg64); + + /* check status for errors */ + reg64 = pnv_i2c_xscom_read(dev->ctlr, I2C_STAT_REG); + g_assert_cmphex(reg64 & I2C_STAT_ANY_ERR, ==, 0); + + /* Read data bytes from fifo register */ + for (byte_num = 0; byte_num < len; byte_num++) { + reg64 = pnv_i2c_xscom_read(dev->ctlr, I2C_FIFO_REG); + buf[byte_num] = GETFIELD(I2C_FIFO, reg64); + } + + /* check status for cmd complete and bus idle */ + reg64 = pnv_i2c_xscom_read(dev->ctlr, I2C_EXTD_STAT_REG); + g_assert_cmphex(reg64 & I2C_EXTD_STAT_I2C_BUSY, ==, 0); + reg64 = pnv_i2c_xscom_read(dev->ctlr, I2C_STAT_REG); + g_assert_cmphex(reg64 & (I2C_STAT_ANY_ERR | I2C_STAT_CMD_COMP), ==, + I2C_STAT_CMD_COMP); +} + +static void pnv_i2c_pca9554_default_cfg(PnvI2cDev *dev) +{ + uint8_t buf[2]; + + /* input register bits are not inverted */ + buf[0] = PCA9554_POLARITY; + buf[1] = 0; + pnv_i2c_send(dev, buf, 2); + + /* All pins are inputs */ + buf[0] = PCA9554_CONFIG; + buf[1] = 0xff; + pnv_i2c_send(dev, buf, 2); + + /* Output value for when pins are outputs */ + buf[0] = PCA9554_OUTPUT; + buf[1] = 0xff; + pnv_i2c_send(dev, buf, 2); +} + +static void pnv_i2c_pca9554_set_pin(PnvI2cDev *dev, int pin, bool high) +{ + uint8_t send_buf[2]; + uint8_t recv_buf[2]; + uint8_t mask = 0x1 << pin; + uint8_t new_value = ((high) ? 1 : 0) << pin; + + /* read current OUTPUT value */ + send_buf[0] = PCA9554_OUTPUT; + pnv_i2c_send(dev, send_buf, 1); + pnv_i2c_recv(dev, recv_buf, 1); + + /* write new OUTPUT value */ + send_buf[1] = (recv_buf[0] & ~mask) | new_value; + pnv_i2c_send(dev, send_buf, 2); + + /* Update config bit for output */ + send_buf[0] = PCA9554_CONFIG; + pnv_i2c_send(dev, send_buf, 1); + pnv_i2c_recv(dev, recv_buf, 1); + send_buf[1] = recv_buf[0] & ~mask; + pnv_i2c_send(dev, send_buf, 2); +} + +static uint8_t pnv_i2c_pca9554_read_pins(PnvI2cDev *dev) +{ + uint8_t send_buf[1]; + uint8_t recv_buf[1]; + uint8_t inputs; + send_buf[0] = PCA9554_INPUT; + pnv_i2c_send(dev, send_buf, 1); + pnv_i2c_recv(dev, recv_buf, 1); + inputs = recv_buf[0]; + return inputs; +} + +static void pnv_i2c_pca9554_flip_polarity(PnvI2cDev *dev) +{ + uint8_t recv_buf[1]; + uint8_t send_buf[2]; + + send_buf[0] = PCA9554_POLARITY; + pnv_i2c_send(dev, send_buf, 1); + pnv_i2c_recv(dev, recv_buf, 1); + send_buf[1] = recv_buf[0] ^ 0xff; + pnv_i2c_send(dev, send_buf, 2); +} + +static void pnv_i2c_pca9554_default_inputs(PnvI2cDev *dev) +{ + uint8_t pin_values = pnv_i2c_pca9554_read_pins(dev); + g_assert_cmphex(pin_values, ==, 0xff); +} + +/* Check that setting pin values and polarity changes inputs as expected */ +static void pnv_i2c_pca554_set_pins(PnvI2cDev *dev) +{ + uint8_t pin_values; + pnv_i2c_pca9554_set_pin(dev, 0, 0); + pin_values = pnv_i2c_pca9554_read_pins(dev); + g_assert_cmphex(pin_values, ==, 0xfe); + pnv_i2c_pca9554_flip_polarity(dev); + pin_values = pnv_i2c_pca9554_read_pins(dev); + g_assert_cmphex(pin_values, ==, 0x01); + pnv_i2c_pca9554_set_pin(dev, 2, 0); + pin_values = pnv_i2c_pca9554_read_pins(dev); + g_assert_cmphex(pin_values, ==, 0x05); + pnv_i2c_pca9554_flip_polarity(dev); + pin_values = pnv_i2c_pca9554_read_pins(dev); + g_assert_cmphex(pin_values, ==, 0xfa); + pnv_i2c_pca9554_default_cfg(dev); + pin_values = pnv_i2c_pca9554_read_pins(dev); + g_assert_cmphex(pin_values, ==, 0xff); +} + +static void pnv_i2c_pca9552_default_cfg(PnvI2cDev *dev) +{ + uint8_t buf[2]; + /* configure pwm/psc regs */ + buf[0] = PCA9552_PSC0; + buf[1] = 0xff; + pnv_i2c_send(dev, buf, 2); + buf[0] = PCA9552_PWM0; + buf[1] = 0x80; + pnv_i2c_send(dev, buf, 2); + buf[0] = PCA9552_PSC1; + buf[1] = 0xff; + pnv_i2c_send(dev, buf, 2); + buf[0] = PCA9552_PWM1; + buf[1] = 0x80; + pnv_i2c_send(dev, buf, 2); + + /* configure all pins as inputs */ + buf[0] = PCA9552_LS0; + buf[1] = 0x55; + pnv_i2c_send(dev, buf, 2); + buf[0] = PCA9552_LS1; + buf[1] = 0x55; + pnv_i2c_send(dev, buf, 2); + buf[0] = PCA9552_LS2; + buf[1] = 0x55; + pnv_i2c_send(dev, buf, 2); + buf[0] = PCA9552_LS3; + buf[1] = 0x55; + pnv_i2c_send(dev, buf, 2); +} + +static void pnv_i2c_pca9552_set_pin(PnvI2cDev *dev, int pin, bool high) +{ + uint8_t send_buf[2]; + uint8_t recv_buf[2]; + uint8_t reg = PCA9552_LS0 + (pin / 4); + uint8_t shift = (pin % 4) * 2; + uint8_t mask = ~(0x3 << shift); + uint8_t new_value = ((high) ? 1 : 0) << shift; + + /* read current LSx value */ + send_buf[0] = reg; + pnv_i2c_send(dev, send_buf, 1); + pnv_i2c_recv(dev, recv_buf, 1); + + /* write new value to LSx */ + send_buf[1] = (recv_buf[0] & mask) | new_value; + pnv_i2c_send(dev, send_buf, 2); +} + +static uint16_t pnv_i2c_pca9552_read_pins(PnvI2cDev *dev) +{ + uint8_t send_buf[2]; + uint8_t recv_buf[2]; + uint16_t inputs; + send_buf[0] = PCA9552_INPUT0; + pnv_i2c_send(dev, send_buf, 1); + pnv_i2c_recv(dev, recv_buf, 1); + inputs = recv_buf[0]; + send_buf[0] = PCA9552_INPUT1; + pnv_i2c_send(dev, send_buf, 1); + pnv_i2c_recv(dev, recv_buf, 1); + inputs |= recv_buf[0] << 8; + return inputs; +} + +static void pnv_i2c_pca9552_default_inputs(PnvI2cDev *dev) +{ + uint16_t pin_values = pnv_i2c_pca9552_read_pins(dev); + g_assert_cmphex(pin_values, ==, 0xffff); +} + +/* + * Set pins 0-4 one at a time and verify that pins 5-9 are + * set to the same value + */ +static void pnv_i2c_pca552_set_pins(PnvI2cDev *dev) +{ + uint16_t pin_values; + + /* set pin 0 low */ + pnv_i2c_pca9552_set_pin(dev, 0, 0); + pin_values = pnv_i2c_pca9552_read_pins(dev); + + /* pins 0 and 5 should be low */ + g_assert_cmphex(pin_values, ==, 0xffde); + + /* set pin 1 low */ + pnv_i2c_pca9552_set_pin(dev, 1, 0); + pin_values = pnv_i2c_pca9552_read_pins(dev); + + /* pins 0, 1, 5 and 6 should be low */ + g_assert_cmphex(pin_values, ==, 0xff9c); + + /* set pin 2 low */ + pnv_i2c_pca9552_set_pin(dev, 2, 0); + pin_values = pnv_i2c_pca9552_read_pins(dev); + + /* pins 0, 1, 2, 5, 6 and 7 should be low */ + g_assert_cmphex(pin_values, ==, 0xff18); + + /* set pin 3 low */ + pnv_i2c_pca9552_set_pin(dev, 3, 0); + pin_values = pnv_i2c_pca9552_read_pins(dev); + + /* pins 0, 1, 2, 3, 5, 6, 7 and 8 should be low */ + g_assert_cmphex(pin_values, ==, 0xfe10); + + /* set pin 4 low */ + pnv_i2c_pca9552_set_pin(dev, 4, 0); + pin_values = pnv_i2c_pca9552_read_pins(dev); + + /* pins 0, 1, 2, 3, 5, 6, 7, 8 and 9 should be low */ + g_assert_cmphex(pin_values, ==, 0xfc00); + + /* reset all pins to the high state */ + pnv_i2c_pca9552_default_cfg(dev); + pin_values = pnv_i2c_pca9552_read_pins(dev); + + /* verify all pins went back to the high state */ + g_assert_cmphex(pin_values, ==, 0xffff); +} + +static void reset_engine(PnvI2cCtlr *ctlr) +{ + pnv_i2c_xscom_write(ctlr, I2C_RESET_I2C_REG, 0); +} + +static void check_i2cm_por_regs(QTestState *qts, const PnvChip *chip) +{ + int engine; + for (engine = 0; engine < chip->num_i2c; engine++) { + PnvI2cCtlr ctlr; + ctlr.qts = qts; + ctlr.chip = chip; + ctlr.engine = engine; + + /* Check version in Extended Status Register */ + uint64_t value = pnv_i2c_xscom_read(&ctlr, I2C_EXTD_STAT_REG); + g_assert_cmphex(value & I2C_EXTD_STAT_I2C_VERSION, ==, 0x1700000000); + + /* Check for command complete and bus idle in Status Register */ + value = pnv_i2c_xscom_read(&ctlr, I2C_STAT_REG); + g_assert_cmphex(value & (I2C_STAT_ANY_ERR | I2C_STAT_CMD_COMP), + ==, + I2C_STAT_CMD_COMP); + } +} + +static void reset_all(QTestState *qts, const PnvChip *chip) +{ + int engine; + for (engine = 0; engine < chip->num_i2c; engine++) { + PnvI2cCtlr ctlr; + ctlr.qts = qts; + ctlr.chip = chip; + ctlr.engine = engine; + reset_engine(&ctlr); + pnv_i2c_xscom_write(&ctlr, I2C_MODE_REG, 0x02be040000000000); + } +} + +static void test_host_i2c(const void *data) +{ + const PnvChip *chip = data; + QTestState *qts; + const char *machine = "powernv8"; + PnvI2cCtlr ctlr; + PnvI2cDev pca9552; + PnvI2cDev pca9554; + + if (chip->chip_type == PNV_CHIP_POWER9) { + machine = "powernv9"; + } else if (chip->chip_type == PNV_CHIP_POWER10) { + machine = "powernv10-rainier"; + } + + qts = qtest_initf("-M %s -smp %d,cores=1,threads=%d -nographic " + "-nodefaults -serial mon:stdio -S " + "-d guest_errors", + machine, SMT, SMT); + + /* Check the I2C master status registers after POR */ + check_i2cm_por_regs(qts, chip); + + /* Now do a forced "immediate" reset on all engines */ + reset_all(qts, chip); + + /* Check that the status values are still good */ + check_i2cm_por_regs(qts, chip); + + /* P9 doesn't have any i2c devices attached at this time */ + if (chip->chip_type != PNV_CHIP_POWER10) { + qtest_quit(qts); + return; + } + + /* Initialize for a P10 pca9552 hotplug device */ + ctlr.qts = qts; + ctlr.chip = chip; + ctlr.engine = 2; + pca9552.ctlr = &ctlr; + pca9552.port = 1; + pca9552.addr = 0x63; + + /* Set all pca9552 pins as inputs */ + pnv_i2c_pca9552_default_cfg(&pca9552); + + /* Check that all pins of the pca9552 are high */ + pnv_i2c_pca9552_default_inputs(&pca9552); + + /* perform individual pin tests */ + pnv_i2c_pca552_set_pins(&pca9552); + + /* Initialize for a P10 pca9554 CableCard Presence detection device */ + pca9554.ctlr = &ctlr; + pca9554.port = 1; + pca9554.addr = 0x25; + + /* Set all pca9554 pins as inputs */ + pnv_i2c_pca9554_default_cfg(&pca9554); + + /* Check that all pins of the pca9554 are high */ + pnv_i2c_pca9554_default_inputs(&pca9554); + + /* perform individual pin tests */ + pnv_i2c_pca554_set_pins(&pca9554); + + qtest_quit(qts); +} + +static void add_test(const char *name, void (*test)(const void *data)) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(pnv_chips); i++) { + char *tname = g_strdup_printf("pnv-xscom/%s/%s", name, + pnv_chips[i].cpu_model); + qtest_add_data_func(tname, &pnv_chips[i], test); + g_free(tname); + } +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + add_test("host-i2c", test_host_i2c); + return g_test_run(); +} diff --git a/tests/qtest/pnv-xscom-test.c b/tests/qtest/pnv-xscom-test.c index 8a5ac11037..c814c0f4f5 100644 --- a/tests/qtest/pnv-xscom-test.c +++ b/tests/qtest/pnv-xscom-test.c @@ -10,66 +10,7 @@ #include "libqtest.h" -typedef enum PnvChipType { - PNV_CHIP_POWER8E, /* AKA Murano (default) */ - PNV_CHIP_POWER8, /* AKA Venice */ - PNV_CHIP_POWER8NVL, /* AKA Naples */ - PNV_CHIP_POWER9, /* AKA Nimbus */ - PNV_CHIP_POWER10, -} PnvChipType; - -typedef struct PnvChip { - PnvChipType chip_type; - const char *cpu_model; - uint64_t xscom_base; - uint64_t cfam_id; - uint32_t first_core; -} PnvChip; - -static const PnvChip pnv_chips[] = { - { - .chip_type = PNV_CHIP_POWER8, - .cpu_model = "POWER8", - .xscom_base = 0x0003fc0000000000ull, - .cfam_id = 0x220ea04980000000ull, - .first_core = 0x1, - }, { - .chip_type = PNV_CHIP_POWER8NVL, - .cpu_model = "POWER8NVL", - .xscom_base = 0x0003fc0000000000ull, - .cfam_id = 0x120d304980000000ull, - .first_core = 0x1, - }, - { - .chip_type = PNV_CHIP_POWER9, - .cpu_model = "POWER9", - .xscom_base = 0x000603fc00000000ull, - .cfam_id = 0x220d104900008000ull, - .first_core = 0x0, - }, - { - .chip_type = PNV_CHIP_POWER10, - .cpu_model = "POWER10", - .xscom_base = 0x000603fc00000000ull, - .cfam_id = 0x120da04900008000ull, - .first_core = 0x0, - }, -}; - -static uint64_t pnv_xscom_addr(const PnvChip *chip, uint32_t pcba) -{ - uint64_t addr = chip->xscom_base; - - if (chip->chip_type == PNV_CHIP_POWER10) { - addr |= ((uint64_t) pcba << 3); - } else if (chip->chip_type == PNV_CHIP_POWER9) { - addr |= ((uint64_t) pcba << 3); - } else { - addr |= (((uint64_t) pcba << 4) & ~0xffull) | - (((uint64_t) pcba << 3) & 0x78); - } - return addr; -} +#include "pnv-xscom.h" static uint64_t pnv_xscom_read(QTestState *qts, const PnvChip *chip, uint32_t pcba) diff --git a/tests/qtest/pnv-xscom.h b/tests/qtest/pnv-xscom.h new file mode 100644 index 0000000000..6f62941744 --- /dev/null +++ b/tests/qtest/pnv-xscom.h @@ -0,0 +1,80 @@ +/* + * PowerNV XSCOM Bus + * + * Copyright (c) 2024, IBM Corporation. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef PNV_XSCOM_H +#define PNV_XSCOM_H + +#define SMT 4 /* some tests will break if less than 4 */ + +typedef enum PnvChipType { + PNV_CHIP_POWER8E, /* AKA Murano (default) */ + PNV_CHIP_POWER8, /* AKA Venice */ + PNV_CHIP_POWER8NVL, /* AKA Naples */ + PNV_CHIP_POWER9, /* AKA Nimbus */ + PNV_CHIP_POWER10, +} PnvChipType; + +typedef struct PnvChip { + PnvChipType chip_type; + const char *cpu_model; + uint64_t xscom_base; + uint64_t cfam_id; + uint32_t first_core; + uint32_t num_i2c; +} PnvChip; + +static const PnvChip pnv_chips[] = { + { + .chip_type = PNV_CHIP_POWER8, + .cpu_model = "POWER8", + .xscom_base = 0x0003fc0000000000ull, + .cfam_id = 0x220ea04980000000ull, + .first_core = 0x1, + .num_i2c = 0, + }, { + .chip_type = PNV_CHIP_POWER8NVL, + .cpu_model = "POWER8NVL", + .xscom_base = 0x0003fc0000000000ull, + .cfam_id = 0x120d304980000000ull, + .first_core = 0x1, + .num_i2c = 0, + }, + { + .chip_type = PNV_CHIP_POWER9, + .cpu_model = "POWER9", + .xscom_base = 0x000603fc00000000ull, + .cfam_id = 0x220d104900008000ull, + .first_core = 0x0, + .num_i2c = 4, + }, + { + .chip_type = PNV_CHIP_POWER10, + .cpu_model = "POWER10", + .xscom_base = 0x000603fc00000000ull, + .cfam_id = 0x120da04900008000ull, + .first_core = 0x0, + .num_i2c = 4, + }, +}; + +static inline uint64_t pnv_xscom_addr(const PnvChip *chip, uint32_t pcba) +{ + uint64_t addr = chip->xscom_base; + + if (chip->chip_type == PNV_CHIP_POWER10) { + addr |= ((uint64_t) pcba << 3); + } else if (chip->chip_type == PNV_CHIP_POWER9) { + addr |= ((uint64_t) pcba << 3); + } else { + addr |= (((uint64_t) pcba << 4) & ~0xffull) | + (((uint64_t) pcba << 3) & 0x78); + } + return addr; +} + +#endif /* PNV_XSCOM_H */ -- cgit 1.4.1