From nobody Thu Jun 25 05:53:05 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=quarantine dis=none) header.from=9elements.com ARC-Seal: i=1; a=rsa-sha256; t=1781870197; cv=none; d=zohomail.com; s=zohoarc; b=cZJnZYId0UTMuOowqdVPmlBegI+cSiyxBvsMFo+qybtI66z//nd8GT8F8/Ch1M5XeBk04YMPBrfWMUNXhC5Xi1OzzX5W68C1G6IejdAIBgRV0WH2sp1wwlEM1Mkyu9sUe+qyW6PqhnvDVasnEXv8AY3ouGBQPxwXvxy2F+2nk8k= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1781870197; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=yJRSrxahRkdWIZv2lvX2AZotDoi8WuISiQJyi/g5p1U=; b=Uq52C+OjTod9udwf56pRpZnWAgi7GBgZnDebIBCfTv9Soa4a9BphwyNMMAyuGj0OfBvyXlvhGu79f7Zdr8lJS6r0gEstDel2EreA5EW5XfyZUUsC0x2Wh7HiANdRJGcpe8NcXWD9SOhOnGJ9fgmklPopuDbbjf+BmvV8r8w0Oyo= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists1p.gnu.org (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1781870197268933.6290502111506; Fri, 19 Jun 2026 04:56:37 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1waXpE-0003qD-IC; Fri, 19 Jun 2026 07:55:56 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists1p.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1waXpC-0003pU-6J for qemu-devel@nongnu.org; Fri, 19 Jun 2026 07:55:54 -0400 Received: from mail-wr1-x436.google.com ([2a00:1450:4864:20::436]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1waXp9-0007IR-7s for qemu-devel@nongnu.org; Fri, 19 Jun 2026 07:55:53 -0400 Received: by mail-wr1-x436.google.com with SMTP id ffacd0b85a97d-462bb734793so1468431f8f.1 for ; Fri, 19 Jun 2026 04:55:50 -0700 (PDT) Received: from localhost.localdomain ([188.111.3.154]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-4923ff821aasm64439975e9.12.2026.06.19.04.55.48 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 19 Jun 2026 04:55:48 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=9elements.com; s=google; t=1781870149; x=1782474949; darn=nongnu.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=yJRSrxahRkdWIZv2lvX2AZotDoi8WuISiQJyi/g5p1U=; b=H0dQMIDOuhhuYlIgxItyhTtE3KY5BESGkqGBO8d9p1YV1CN4QwyZsnA2FjMiCiyoFQ oN4EHdK0+436mM+6T2ht2YazRHvk2KmFwLsanFqeRaZkjTwZFW2WufssrwSIFhFLte4e upiW7Bn2pE3rKmEqyrM3VWhXojwqnK6nlkTxvmB6uOVvW6OrdmWUgclHdj4I06LBc0PR t8+GrYCuL7zKzlES1QWDlDw4zTZWFHdBFhjadbLqjjrNgv79V/1op8CQotv3hdzSXmiQ EGTVZO2Y+ZIbYGuKReYhnj/WOk5XeZrbUfXaloG1n4wanhA0QYHAWSzinuVuQjy/Zh3Z QXNQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1781870149; x=1782474949; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=yJRSrxahRkdWIZv2lvX2AZotDoi8WuISiQJyi/g5p1U=; b=kdyX793JGzDi4vKvIv/2FZ4vuLiZa1MHAcBJ9eVvUqxyIKyffZCHyoXukCZP2hqYEK qz7xpyyvPHhkOxIxZB8Hg3giQ+LeRnXuNkWX/BxQA/og9Y25X2CVCfA8TywG8FkkxcHi ONu0MyvB3MNf/HSfWxqvt751urgzP0OrA+ls+MZV3+pGNuusI4H0rlYEBBIvXdZdAZ+o K46Qa2Qb5BsoXq9hI8LDnxCeAcsVXpDbJYkAqjB3x9/c2Tx5HBKK/ubkhn9Lyu621ac/ dO2i7AQntynXyT0Kt32nimXX0yAVIvP7IVabygC4cGngXysARe3iaImnZer+ZhUxFWou l1Lg== X-Gm-Message-State: AOJu0YyRePuKSNIZpxvko34Z9oe3xa2tVgFhGG+Clc8H4k+zIe1j1ZZu 5oa6S7cw1chHSFO+IM78E4glalQ4wQEbEWrtBjIOEpIxt8pzTuSL4Nt9mgGF5UtFR44FsyR6IB5 sRqFGu6k= X-Gm-Gg: AfdE7cnyCNNPPfExBUyiNbTP2sKAOzeTlLDIKYhQgiQN978n+fmRTOC2enZ9dxdSdYf UPbHtDu2tqyciRdW3MfKQiqgKmF1PxiOLawnVX/RDcTXINXPX356Qafi04dAeu8lM++Qz60Q7o2 Q0vSPvgMvhXGcnzWbAYXWnB/fFfR/BfRIfAGdfeNr9QX9YvpaWhne2NNF+u0EmcUB9PjT9gRKrT ron7nsFmT5qErygbM0Y4MyLFsMGFS48afTJzsxewVlyGef/IAW013aDzcgF59hmyqPlcH5vmvtd qh9n7BxeCKiZIKtzEZ0eeO5w9alYBz2XeQAbNCDR0poJvRMXGW8BUQ7i3CP5XkwPM1aoJ2/kFRi uMLNp63J7vxifm23QS6oMWn+On87Ma6fZrVLquz5xD2TMSD+pPMB34cavrKRovf7VJ+GjwEZmWF +6khdLbIqVArnzOObhB0CRsthEWdCsH1YJc4qZbA== X-Received: by 2002:a05:600c:8b52:b0:492:379c:32ee with SMTP id 5b1f17b1804b1-4923ef4c629mr66848015e9.6.1781870149259; Fri, 19 Jun 2026 04:55:49 -0700 (PDT) From: To: qemu-devel@nongnu.org Cc: eblot@meta.com, titusr@google.com, clg@kaod.org, Alexander Hansen , Peter Maydell , Steven Lee , Troy Lee , Jamin Lin , Kane Chen , Andrew Jeffery , Joel Stanley , qemu-arm@nongnu.org, =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Subject: [PATCH v7 1/4] ast2600: yosemite4 initial support Date: Fri, 19 Jun 2026 13:55:30 +0200 Message-ID: <20260619115533.5664-2-alexander.hansen@9elements.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260619115533.5664-1-alexander.hansen@9elements.com> References: <20260619115533.5664-1-alexander.hansen@9elements.com> MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists1p.gnu.org; Received-SPF: pass client-ip=2a00:1450:4864:20::436; envelope-from=alexander.hansen@9elements.com; helo=mail-wr1-x436.google.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=unavailable autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @9elements.com) X-ZM-MESSAGEID: 1781870198495158500 From: Alexander Hansen Initial patch based on [1] to support yosemite v4 bmc emulation. The goal of this machine support is to support OpenBMC development. Reference linux devicetree from openbmc linux is [2]. Status: - Enclosure FRU: showing up - Management Board FRU: showing up - Blade Board FRU: showing up - Blade Board sensors: not implemented - Blade Chassis FRU: showing up - Fan Board FRU: both showing up - Fan Board sensors: supported Overall the emulation is incomplete but already helpful in development and testing. The focus of this initial support is on the FRU eeproms and fanboard sensors. Tested: booted an OpenBMC image for yosemite4 target. References: [1] https://github.com/9elements/qemu/commit/32139f913c2bd0ebe4bd26c4676586= 1f6f9f2d49 [2] arch/arm/boot/dts/aspeed/aspeed-bmc-facebook-yosemite4.dts [3] https://github.com/legoater/qemu-aspeed-boot/pull/5 Cc: Emmanuel Blot Cc: Titus Rwantare Cc: "C=C3=A9dric Le Goater" (maintainer:ASPEED BMCs) Cc: Peter Maydell (maintainer:ASPEED BMCs) Cc: Steven Lee (reviewer:ASPEED BMCs) Cc: Troy Lee (reviewer:ASPEED BMCs) Cc: Jamin Lin (reviewer:ASPEED BMCs) Cc: Kane Chen (reviewer:ASPEED BMCs) Cc: Andrew Jeffery (reviewer:ASPEED BMCs) Cc: Joel Stanley (reviewer:ASPEED BMCs) Cc: qemu-arm@nongnu.org (open list:ASPEED BMCs) Cc: qemu-devel@nongnu.org (open list:All patches CC here) Reviewed-by: C=C3=A9dric Le Goater Signed-off-by: Alexander Hansen --- docs/system/arm/aspeed.rst | 5 +- hw/arm/aspeed_ast2600_fby4.c | 260 +++++++++++++++++++++++++++++++++++ hw/arm/meson.build | 1 + 3 files changed, 264 insertions(+), 2 deletions(-) create mode 100644 hw/arm/aspeed_ast2600_fby4.c diff --git a/docs/system/arm/aspeed.rst b/docs/system/arm/aspeed.rst index 2d51ceeb84..f719a5cebb 100644 --- a/docs/system/arm/aspeed.rst +++ b/docs/system/arm/aspeed.rst @@ -1,5 +1,5 @@ -Aspeed family boards (``anacapa-bmc``, ``ast2500-evb``, ``ast2600-evb``, `= `bletchley-bmc``, ``fuji-bmc``, ``gb200nvl-bmc``, ``fby35-bmc``, ``g220a-bm= c``, ``palmetto-bmc``, ``quanta-q71l-bmc``, ``rainier-bmc``, ``romulus-bmc`= `, ``supermicrox11-bmc``, ``supermicrox11spi-bmc``, ``tiogapass-bmc``, ``wi= therspoon-bmc``, ``yosemitev2-bmc``) -=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D +Aspeed family boards (``anacapa-bmc``, ``ast2500-evb``, ``ast2600-evb``, `= `bletchley-bmc``, ``fuji-bmc``, ``gb200nvl-bmc``, ``fby35-bmc``, ``fby4-bmc= ``, ``g220a-bmc``, ``palmetto-bmc``, ``quanta-q71l-bmc``, ``rainier-bmc``, = ``romulus-bmc``, ``supermicrox11-bmc``, ``supermicrox11spi-bmc``, ``tiogapa= ss-bmc``, ``witherspoon-bmc``, ``yosemitev2-bmc``) +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D =20 The QEMU Aspeed machines model BMCs of various OpenPOWER systems and Aspeed evaluation boards. They are based on different releases of the @@ -33,6 +33,7 @@ AST2600 SoC based machines : - ``fuji-bmc`` Facebook Fuji BMC - ``bletchley-bmc`` Facebook Bletchley BMC - ``fby35-bmc`` Facebook fby35 BMC +- ``fby4-bmc`` Facebook fby4 BMC - ``anacapa-bmc`` Facebook Anacapa BMC - ``gb200nvl-bmc`` Nvidia GB200nvl BMC =20 diff --git a/hw/arm/aspeed_ast2600_fby4.c b/hw/arm/aspeed_ast2600_fby4.c new file mode 100644 index 0000000000..03f54d4393 --- /dev/null +++ b/hw/arm/aspeed_ast2600_fby4.c @@ -0,0 +1,260 @@ +/* + * Facebook Yosemite V4 + * + * Copyright 2026 9elements. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "hw/arm/machines-qom.h" +#include "hw/arm/aspeed.h" +#include "hw/arm/aspeed_soc.h" +#include "hw/nvram/eeprom_at24c.h" +#include "hw/i2c/i2c_mux_pca954x.h" +#include "hw/gpio/pca9552.h" + +#define FBY4_BMC_RAM_SIZE ASPEED_RAM_SIZE(2 * GiB) + +/* START OF EEPROM CONTENTS */ + +/* + *./frugen -s board.mfg=3D"Wiwynn" \ + * --set board.pname=3D"Fan Board FSC-MAX ADC-TI LED-NXP EFUSE-MAX" \ + * --set text:board.pn=3D"BRD-PN-345" \ + * --board-date "10/1/2017 12:58:00" \ + * --set board.serial=3D"123456" \ + * --set product.pname=3D"Yosemite V4" \ + * --set product.mfg=3D"Wiwynn" \ + * --set product.ver=3D"v1.1" \ + * --set product.serial=3D"123456" \ + * --set product.atag=3D"PLACEHOLDER" \ + * --set product.custom=3D"Fanboard Custom1" \ + * --set product.custom.1=3D"Fanboard Custom2" \ + * --set text:product.pn=3D"PN-345" \ + * fru-yv4-fanboard.bin + */ +/* EM Config: yosemite4_fanboard_fsc_max_adc_ti_led_nxp_ons_efuse_max.json= */ +/* Yosemite4 fan board */ +static const uint8_t fru_yv4_fanboard_bin[] =3D { + 0x01, 0x00, 0x00, 0x01, 0x0b, 0x00, 0x00, 0xf3, 0x01, 0x0a, 0x19, 0xce, + 0xc2, 0xa8, 0xc6, 0x57, 0x69, 0x77, 0x79, 0x6e, 0x6e, 0xea, 0x46, 0x61, + 0x6e, 0x20, 0x42, 0x6f, 0x61, 0x72, 0x64, 0x20, 0x46, 0x53, 0x43, 0x2d, + 0x4d, 0x41, 0x58, 0x20, 0x41, 0x44, 0x43, 0x2d, 0x54, 0x49, 0x20, 0x4c, + 0x45, 0x44, 0x2d, 0x4e, 0x58, 0x50, 0x20, 0x45, 0x46, 0x55, 0x53, 0x45, + 0x2d, 0x4d, 0x41, 0x58, 0x85, 0x91, 0x34, 0x51, 0x95, 0x05, 0xca, 0x42, + 0x52, 0x44, 0x2d, 0x50, 0x4e, 0x2d, 0x33, 0x34, 0x35, 0xc0, 0xc1, 0x00, + 0x00, 0x00, 0x00, 0xdb, 0x01, 0x09, 0x19, 0xc6, 0x57, 0x69, 0x77, 0x79, + 0x6e, 0x6e, 0xcb, 0x59, 0x6f, 0x73, 0x65, 0x6d, 0x69, 0x74, 0x65, 0x20, + 0x56, 0x34, 0xc6, 0x50, 0x4e, 0x2d, 0x33, 0x34, 0x35, 0xc4, 0x76, 0x31, + 0x2e, 0x31, 0x85, 0x91, 0x34, 0x51, 0x95, 0x05, 0x89, 0x30, 0x1b, 0x8e, + 0x25, 0xfa, 0xb2, 0x64, 0x29, 0x03, 0xc0, 0xd0, 0x46, 0x61, 0x6e, 0x62, + 0x6f, 0x61, 0x72, 0x64, 0x20, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x32, + 0xc1, 0x00, 0x00, 0x9d +}; +static const size_t fru_yv4_fanboard_bin_len =3D sizeof(fru_yv4_fanboard_b= in); + +/* + *./frugen -s board.mfg=3D"Wiwynn" \ + * --set board.pname=3D"Sentinel Dome without Retimer" \ + * --set text:board.pn=3D"BRD-PN-345" \ + * --board-date "10/1/2017 12:58:00" \ + * --set board.serial=3D"123456" \ + * --set product.pname=3D"Yosemite V4" \ + * --set product.mfg=3D"Wiwynn" \ + * --set product.ver=3D"v1.1" \ + * --set product.serial=3D"123456" \ + * --set product.atag=3D"PLACEHOLDER" \ + * --set product.custom=3D"Yosemite V4 T1" \ + * --set product.custom.1=3D"Yosemite V4 T1" \ + * --set text:product.pn=3D"PN-345" \ + * fru-yv4-sentineldome-board.bin + */ +/* product.cust maps to PRODUCT_INFO_AM2 */ +/* EM Config: yosemite4_sentineldome_t1.json */ +/* Yosemite4 Sentinel Dome without Retimer Boards */ +static const uint8_t fru_yv4_sentineldome_board_bin[] =3D { + 0x01, 0x00, 0x00, 0x01, 0x09, 0x00, 0x00, 0xf5, 0x01, 0x08, 0x19, 0xce, + 0xc2, 0xa8, 0xc6, 0x57, 0x69, 0x77, 0x79, 0x6e, 0x6e, 0xdd, 0x53, 0x65, + 0x6e, 0x74, 0x69, 0x6e, 0x65, 0x6c, 0x20, 0x44, 0x6f, 0x6d, 0x65, 0x20, + 0x77, 0x69, 0x74, 0x68, 0x6f, 0x75, 0x74, 0x20, 0x52, 0x65, 0x74, 0x69, + 0x6d, 0x65, 0x72, 0x85, 0x91, 0x34, 0x51, 0x95, 0x05, 0xca, 0x42, 0x52, + 0x44, 0x2d, 0x50, 0x4e, 0x2d, 0x33, 0x34, 0x35, 0xc0, 0xc1, 0x00, 0x78, + 0x01, 0x09, 0x19, 0xc6, 0x57, 0x69, 0x77, 0x79, 0x6e, 0x6e, 0xcb, 0x59, + 0x6f, 0x73, 0x65, 0x6d, 0x69, 0x74, 0x65, 0x20, 0x56, 0x34, 0xc6, 0x50, + 0x4e, 0x2d, 0x33, 0x34, 0x35, 0xc4, 0x76, 0x31, 0x2e, 0x31, 0x85, 0x91, + 0x34, 0x51, 0x95, 0x05, 0x89, 0x30, 0x1b, 0x8e, 0x25, 0xfa, 0xb2, 0x64, + 0x29, 0x03, 0xc0, 0xce, 0x59, 0x6f, 0x73, 0x65, 0x6d, 0x69, 0x74, 0x65, + 0x20, 0x56, 0x34, 0x20, 0x54, 0x31, 0xc1, 0x00, 0x00, 0x00, 0x00, 0xeb +}; +static const size_t fru_yv4_sentineldome_board_bin_len =3D + sizeof(fru_yv4_sentineldome_board_bin); + +/* + *./frugen -s board.mfg=3D"Wiwynn" \ + * --set board.pname=3D"Sentinel Dome" \ + * --set text:board.pn=3D"BRD-PN-345" \ + * --board-date "10/1/2017 12:58:00" \ + * --set board.serial=3D"123456" \ + * --set product.pname=3D"Yosemite V4" \ + * --set product.mfg=3D"Wiwynn" \ + * --set product.ver=3D"v1.1" \ + * --set product.serial=3D"123456" \ + * --set product.atag=3D"PLACEHOLDER" \ + * --set text:product.pn=3D"PN-345" \ + * fru-yv4-sentineldome-chassis.bin + */ +/* EM Config: yosemite4_sentineldome_chassis.json */ +/* Yosemite 4 Sentinel Dome Chassis FRU */ +static const uint8_t fru_yv4_sentineldome_chassis_bin[] =3D { + 0x01, 0x00, 0x00, 0x01, 0x07, 0x00, 0x00, 0xf7, 0x01, 0x06, 0x19, 0xce, + 0xc2, 0xa8, 0xc6, 0x57, 0x69, 0x77, 0x79, 0x6e, 0x6e, 0xcd, 0x53, 0x65, + 0x6e, 0x74, 0x69, 0x6e, 0x65, 0x6c, 0x20, 0x44, 0x6f, 0x6d, 0x65, 0x85, + 0x91, 0x34, 0x51, 0x95, 0x05, 0xca, 0x42, 0x52, 0x44, 0x2d, 0x50, 0x4e, + 0x2d, 0x33, 0x34, 0x35, 0xc0, 0xc1, 0x00, 0xb6, 0x01, 0x07, 0x19, 0xc6, + 0x57, 0x69, 0x77, 0x79, 0x6e, 0x6e, 0xcb, 0x59, 0x6f, 0x73, 0x65, 0x6d, + 0x69, 0x74, 0x65, 0x20, 0x56, 0x34, 0xc6, 0x50, 0x4e, 0x2d, 0x33, 0x34, + 0x35, 0xc4, 0x76, 0x31, 0x2e, 0x31, 0x85, 0x91, 0x34, 0x51, 0x95, 0x05, + 0x89, 0x30, 0x1b, 0x8e, 0x25, 0xfa, 0xb2, 0x64, 0x29, 0x03, 0xc0, 0xc1, + 0x00, 0x00, 0x00, 0x59 +}; +static const size_t fru_yv4_sentineldome_chassis_bin_len =3D + sizeof(fru_yv4_sentineldome_chassis_bin); + +/* + *./frugen -s board.mfg=3D"Wiwynn" \ + * --set board.pname=3D"Management Board wBMC" \ + * --set text:board.pn=3D"BRD-PN-345" \ + * --board-date "10/1/2017 12:58:00" \ + * --set board.serial=3D"123456" \ + * --set product.pname=3D"Yosemite V4" \ + * --set product.mfg=3D"Wiwynn" \ + * --set product.ver=3D"v1.1" \ + * --set product.serial=3D"123456" \ + * --set product.atag=3D"PLACEHOLDER" \ + * --set text:product.pn=3D"PN-345" \ + * fru-yv4-eclosure.bin + */ +/* EM Config: yosemite4_chassis.json, yosemite4.json */ +/* Yosemite 4 Sentinel Dome Enclosure FRU */ +static const uint8_t fru_yv4_eclosure_bin[] =3D { + 0x01, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0xf6, 0x01, 0x07, 0x19, 0xce, + 0xc2, 0xa8, 0xc6, 0x57, 0x69, 0x77, 0x79, 0x6e, 0x6e, 0xd5, 0x4d, 0x61, + 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x42, 0x6f, 0x61, + 0x72, 0x64, 0x20, 0x77, 0x42, 0x4d, 0x43, 0x85, 0x91, 0x34, 0x51, 0x95, + 0x05, 0xca, 0x42, 0x52, 0x44, 0x2d, 0x50, 0x4e, 0x2d, 0x33, 0x34, 0x35, + 0xc0, 0xc1, 0x00, 0x26, 0x01, 0x07, 0x19, 0xc6, 0x57, 0x69, 0x77, 0x79, + 0x6e, 0x6e, 0xcb, 0x59, 0x6f, 0x73, 0x65, 0x6d, 0x69, 0x74, 0x65, 0x20, + 0x56, 0x34, 0xc6, 0x50, 0x4e, 0x2d, 0x33, 0x34, 0x35, 0xc4, 0x76, 0x31, + 0x2e, 0x31, 0x85, 0x91, 0x34, 0x51, 0x95, 0x05, 0x89, 0x30, 0x1b, 0x8e, + 0x25, 0xfa, 0xb2, 0x64, 0x29, 0x03, 0xc0, 0xc1, 0x00, 0x00, 0x00, 0x59 +}; +static const size_t fru_yv4_eclosure_bin_len =3D sizeof(fru_yv4_eclosure_b= in); + +/* END OF EEPROM CONTENTS */ + +static void fby4_i2c_init_fanboard(I2CSlave *fan_mux, size_t eepromSize) +{ + /* 2 fan boards */ + for (int i =3D 0; i <=3D 1; i++) { + /* downstream bus */ + I2CBus *bus =3D pca954x_i2c_get_bus(fan_mux, i); + + /* ti,adc128d818 @ 0x1f (adc) */ + /* TODO */ + + /* maxim,max31790 @ 0x20 (pwm) */ + /* TODO */ + + /* + * ti,tca6424 @ 0x22 (gpio) + * linux handles tca6424 with PCA953X_TYPE, same as pca9535 + * { "pca9535", 16 | PCA953X_TYPE | PCA_INT, }, + * { "tca6424", 24 | PCA953X_TYPE | PCA_INT, }, + * so we _could_ be fine here unless more than 16 gpios are used + */ + i2c_slave_create_simple(bus, TYPE_PCA9535, 0x22); + + /* + * NOTE: above works and could use for gpio presence: + * $ gpioget gpiochip2 2 + * 1 + */ + + /* maxim,max31790 @ 0x2f (pwm) */ + /* TODO */ + + /* maxim,max11615 @ 0x33 (adc) */ + /* TODO */ + + at24c_eeprom_init_rom( + bus, 0x52, eepromSize, + fru_yv4_fanboard_bin, + fru_yv4_fanboard_bin_len); + + /* LED blink driver / gpio expander */ + /* nxp,pca9552 @ 0x61 (gpio) */ + i2c_slave_create_simple(bus, TYPE_PCA9552, 0x61); + } +} + +static void fby4_i2c_init_blade_chassis(I2CBus *bus, size_t eepromSize) +{ + /* Sentinel Dome Blade EEPROMS */ + + /* Board */ + at24c_eeprom_init_rom(bus, 0x54, eepromSize, + fru_yv4_sentineldome_board_bin, fru_yv4_sentineldome_board_bin_len= ); + + /* Chassis */ + at24c_eeprom_init_rom(bus, 0x55, eepromSize, + fru_yv4_sentineldome_chassis_bin, fru_yv4_sentineldome_chassis_bin_= len); +} + +static void fby4_i2c_init(AspeedMachineState *bmc) +{ + AspeedSoCState *soc =3D bmc->soc; + I2CBus *i2c[16]; + + for (int i =3D 0; i < 16; i++) { + i2c[i] =3D aspeed_i2c_get_bus(&soc->i2c, i); + } + + /* Enclosure (EM Config: yosemite4_chassis.json, yosemite4.json) */ + at24c_eeprom_init_rom(i2c[1], 0x51, 128 * KiB, + fru_yv4_eclosure_bin, fru_yv4_eclosure_bin_len); + + /* there is 8 blade chassis, but we only emulate 2 for performance rea= son */ + fby4_i2c_init_blade_chassis(i2c[1], 128 * KiB); + fby4_i2c_init_blade_chassis(i2c[2], 128 * KiB); + + /* Yv4 fanboard connection */ + I2CSlave *fan_mux =3D i2c_slave_create_simple(i2c[14], TYPE_PCA9546, 0= x74); + + fby4_i2c_init_fanboard(fan_mux, 128 * KiB); +} + +static void aspeed_machine_fby4_class_init(ObjectClass *oc, const void *da= ta) +{ + MachineClass *mc =3D MACHINE_CLASS(oc); + AspeedMachineClass *amc =3D ASPEED_MACHINE_CLASS(oc); + + mc->desc =3D "Facebook fby4 BMC (Cortex-A7)"; + amc->fmc_model =3D "mx66l1g45g"; + amc->num_cs =3D 2; + amc->macs_mask =3D ASPEED_MAC3_ON; + amc->i2c_init =3D fby4_i2c_init; + mc->default_ram_size =3D FBY4_BMC_RAM_SIZE; + aspeed_machine_class_init_cpus_defaults(mc); +} + +static const TypeInfo aspeed_ast2600_fby4_types[] =3D { + { + .name =3D MACHINE_TYPE_NAME("fby4-bmc"), + .parent =3D MACHINE_TYPE_NAME("ast2600-evb"), + .class_init =3D aspeed_machine_fby4_class_init, + .interfaces =3D arm_machine_interfaces, + } +}; + +DEFINE_TYPES(aspeed_ast2600_fby4_types) diff --git a/hw/arm/meson.build b/hw/arm/meson.build index 4233a800be..64c399c0e8 100644 --- a/hw/arm/meson.build +++ b/hw/arm/meson.build @@ -62,6 +62,7 @@ arm_common_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: fil= es( 'aspeed_ast2600_catalina.c', 'aspeed_ast2600_evb.c', 'aspeed_ast2600_fby35.c', + 'aspeed_ast2600_fby4.c', 'aspeed_ast2600_fuji.c', 'aspeed_ast2600_gb200nvl.c', 'aspeed_ast2600_rainier.c', --=20 2.54.0 From nobody Thu Jun 25 05:53:05 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=quarantine dis=none) header.from=9elements.com ARC-Seal: i=1; a=rsa-sha256; t=1781870193; cv=none; d=zohomail.com; s=zohoarc; b=d39rRq8ii5ZJ0KBtZNplAhLjaDqglJCHXzIuJ7nnCuokBApucDvVX6aV9gFu2kRcwQT4DgBbU4OVNcjz8W0CYCthf9H0TT+ZjXxptLTDhVpmS9SayKcrxN4KuiaPAsAxAlVfeSXAoR7rXZaC3q0XZE6SfnWWPxnR4hhuD7/769k= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1781870193; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=4gazc7DoCz2maUunxSRhWfMPAB4XKEzEdNOyTzDmIFM=; b=Mx2KXo+jak1yHF/gkSTA11+3azdxOx9UlhUTRCiwNt97kZp1v8f5ctsKzXWb2DGK7w6AM/kwG/PF0ozPV27Qg95fGmAhUJfnb6CZNMSYY9MhoTSlJV3G780CdASCqpTeFv5Q16kNVFXqvmUfVzTrniOeJreo8jcCYJObprgH9Qg= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists1p.gnu.org (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1781870193192430.2156538759913; Fri, 19 Jun 2026 04:56:33 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1waXpE-0003qH-Mr; Fri, 19 Jun 2026 07:55:56 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists1p.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1waXpD-0003px-L1 for qemu-devel@nongnu.org; Fri, 19 Jun 2026 07:55:55 -0400 Received: from mail-wm1-x32d.google.com ([2a00:1450:4864:20::32d]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1waXpB-0007LL-KJ for qemu-devel@nongnu.org; Fri, 19 Jun 2026 07:55:55 -0400 Received: by mail-wm1-x32d.google.com with SMTP id 5b1f17b1804b1-49222fb062bso19528375e9.1 for ; Fri, 19 Jun 2026 04:55:53 -0700 (PDT) Received: from localhost.localdomain ([188.111.3.154]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-4923ff821aasm64439975e9.12.2026.06.19.04.55.50 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 19 Jun 2026 04:55:51 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=9elements.com; s=google; t=1781870152; x=1782474952; darn=nongnu.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=4gazc7DoCz2maUunxSRhWfMPAB4XKEzEdNOyTzDmIFM=; b=FOz3msgUxi0JNmxGI53USUuKhagfbCX9WaNN6bIamFYp7A5P42Q5yca+6nX3LorMPR sJQfCfgZA2N86g0m6nRIM5r4Bc3pKnwFOUWIhx30nJiQ6D4AeTiCnGM4yaqzWXAsfW9v 7Ht+GlLrDC3/38RZ9RRWHOL7URWsP8qFvSZQXvw9dSygOFmEsKhnkn74LIC1+HtupRsr gs1sZlnb5hgQM6WTPxhCGNSDNl/yyfRlLqYxn6JJLFAYdld2Q1jveogOG8++gnJCFfyB Z1t8u2jX9jHDpMyRhZxdGVrkxDEI6XQh4BBKV4HsLKUwCxbBssRd6I8ZcnY+XOf/WKGn geLw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1781870152; x=1782474952; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=4gazc7DoCz2maUunxSRhWfMPAB4XKEzEdNOyTzDmIFM=; b=tU92/4qz57dnanjFJMhBcdMAoAk6UT/KnrvUaPnlvaGNBib5oLMAU5XoRbR5QDIsOL J2NyYDVto0W53ZiiQGcw9o2tYjzF9oXnki9ch4n1GUG5dNMoa8r23fsY1ZtlYpZMqAAi yx68EF86cC3mZ39pLy62YDEBiUijt5+5mzGHTns7CV0n+DVzclbflnauucrZQq7brcqp i0g5NQSmWJnUgWPv0tsxjYP0p6rUvqj4Ow+JYsn3C7sMKEqz2v4FPxbsXPvvJbeq4gB9 h3EQ/w4nKJ84BSGgcWB2uR9il1IdufWyG7ePL0s/b0s8e+W8EXhBobXVk222yjVXDxZA XRGQ== X-Gm-Message-State: AOJu0YwWZHc9OANdwq70hHyjx7aW6z4QrHnt0nossU149rhIqDSOo8m3 C2jUBaIWTPVlt8gK3/haA7H5tJa0Lqg1psN8LLwecikFShP5sISTHHKJAZkVBOLg0jEyjmd/o52 x4mHODlI= X-Gm-Gg: AfdE7cmNtEBI+Xxu1n5hFkM3K8lYwVc3ECX2i0uyf5OXRoc6QekAllZ1iGPtufd/cbc 1RR7coAcfiOzgylRoH7Yu3g6XtL9QIT/tEWD9VIpjzqRkIXpy/F8iG0gqmNSDtkB5moiZbukGa6 1wnYZHxafdTU8mOqLIRCLKr2TMiLH+ys8B8ilrdkYfuussyBp8pqOjNyHsISCJC7Um4RbCoktpq YSnuzFxZsgvzaPHgEN4DCXcMe6yGQQiPHpoWFjOMEmrBwSBhgPA/Xw2/WkbqPoLccV3KHj2qj5f kp+AzgBzX3fVOOxflfZxNfm4fY65ALO7POVpzTZpqVX5JXFm47sgUzf0tPZ9OvsGPrPYOoNzzG+ ddfdJZpez+nIPNEKulrbCO+CFUV8VnYpo2jTKZDU4b+TxkBQHx/Gjv1ZMD4QbuF+vS2DDPbP875 ZYA4bzXYCl6UVRy/f52J0y8j5tANtk8XPrs96VYQ== X-Received: by 2002:a05:600c:8716:b0:488:b187:3c with SMTP id 5b1f17b1804b1-49240e4635emr51719175e9.14.1781870152044; Fri, 19 Jun 2026 04:55:52 -0700 (PDT) From: To: qemu-devel@nongnu.org Cc: eblot@meta.com, titusr@google.com, clg@kaod.org, Alexander Hansen , Peter Maydell , Steven Lee , Troy Lee , Jamin Lin , Kane Chen , Andrew Jeffery , Joel Stanley , qemu-arm@nongnu.org, =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Subject: [PATCH v7 2/4] ast2600: yosemite4 functional test Date: Fri, 19 Jun 2026 13:55:31 +0200 Message-ID: <20260619115533.5664-3-alexander.hansen@9elements.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260619115533.5664-1-alexander.hansen@9elements.com> References: <20260619115533.5664-1-alexander.hansen@9elements.com> MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists1p.gnu.org; Received-SPF: pass client-ip=2a00:1450:4864:20::32d; envelope-from=alexander.hansen@9elements.com; helo=mail-wm1-x32d.google.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 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: qemu development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @9elements.com) X-ZM-MESSAGEID: 1781870194321158501 From: Alexander Hansen Add a functional test for the Yosemite V4 (fby4-bmc) machine that validates: - U-Boot initialization and kernel boot sequence The test uses an OpenBMC image built for the yosemite4 board and validates console output through the boot process. Cc: Emmanuel Blot Cc: Titus Rwantare Cc: "C=C3=A9dric Le Goater" (maintainer:ASPEED BMCs) Cc: Peter Maydell (maintainer:ASPEED BMCs) Cc: Steven Lee (reviewer:ASPEED BMCs) Cc: Troy Lee (reviewer:ASPEED BMCs) Cc: Jamin Lin (reviewer:ASPEED BMCs) Cc: Kane Chen (reviewer:ASPEED BMCs) Cc: Andrew Jeffery (reviewer:ASPEED BMCs) Cc: Joel Stanley (reviewer:ASPEED BMCs) Cc: qemu-arm@nongnu.org (open list:ASPEED BMCs) Cc: qemu-devel@nongnu.org (open list:All patches CC here) Reviewed-by: C=C3=A9dric Le Goater Signed-off-by: Alexander Hansen --- tests/functional/arm/meson.build | 2 ++ tests/functional/arm/test_aspeed_fby4.py | 45 ++++++++++++++++++++++++ 2 files changed, 47 insertions(+) create mode 100755 tests/functional/arm/test_aspeed_fby4.py diff --git a/tests/functional/arm/meson.build b/tests/functional/arm/meson.= build index 61b00932db..19331b3f16 100644 --- a/tests/functional/arm/meson.build +++ b/tests/functional/arm/meson.build @@ -15,6 +15,7 @@ test_arm_timeouts =3D { 'aspeed_anacapa' : 480, 'aspeed_bletchley' : 480, 'aspeed_catalina' : 480, + 'aspeed_fby4': 480, 'aspeed_gb200nvl_bmc' : 480, 'aspeed_rainier' : 480, 'bpim2u' : 500, @@ -50,6 +51,7 @@ tests_arm_system_thorough =3D [ 'aspeed_anacapa', 'aspeed_bletchley', 'aspeed_catalina', + 'aspeed_fby4', 'aspeed_gb200nvl_bmc', 'aspeed_rainier', 'bpim2u', diff --git a/tests/functional/arm/test_aspeed_fby4.py b/tests/functional/ar= m/test_aspeed_fby4.py new file mode 100755 index 0000000000..a521834f79 --- /dev/null +++ b/tests/functional/arm/test_aspeed_fby4.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +# +# Functional test that boots the ASPEED machines +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import Asset +from qemu_test import wait_for_console_pattern +from aspeed import AspeedTest + + +class YosemiteV4Machine(AspeedTest): + + ASSET_YOSEMITE_V4_FLASH =3D Asset( + 'https://github.com/legoater/qemu-aspeed-boot/raw/refs/heads/maste= r/images/yosemite4-bmc/openbmc-20260505132843/obmc-phosphor-image-yosemite4= -20260505132843.static.mtd.xz', + 'dff6946363b41f952b15cfc3156482b89fcfc1b0ecfc3ec8b3ed496a5f001ef9') + + def do_test_arm_aspeed_openbmc_no_network(self, machine, image, uboot, + cpu_id, soc): + + self.set_machine(machine) + self.vm.set_console() + self.vm.add_args('-drive', f'file=3D{image},if=3Dmtd,format=3Draw', + '-snapshot') + self.vm.launch() + + self.wait_for_console_pattern(f'U-Boot {uboot}') + self.wait_for_console_pattern('## Loading kernel from FIT Image') + self.wait_for_console_pattern('Starting kernel ...') + self.wait_for_console_pattern(f'Booting Linux on physical CPU {cpu= _id}') + self.wait_for_console_pattern(f'ASPEED {soc}') + self.wait_for_console_pattern('/init as init process') + # yosemite v4 does not emit the hostname log which is + # different from the other machines. + self.wait_for_console_pattern('yosemite4 login:') + + def test_arm_ast2600_yosemitev4_openbmc(self): + image_path =3D self.uncompress(self.ASSET_YOSEMITE_V4_FLASH) + + self.do_test_arm_aspeed_openbmc_no_network('fby4-bmc', image=3Dima= ge_path, + uboot=3D'2019.04', cpu_id=3D'0xf00= ', + soc=3D'AST2600 rev A3') + +if __name__ =3D=3D '__main__': + AspeedTest.main() --=20 2.54.0 From nobody Thu Jun 25 05:53:05 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=quarantine dis=none) header.from=9elements.com ARC-Seal: i=1; a=rsa-sha256; t=1781870204; cv=none; d=zohomail.com; s=zohoarc; b=k0S94nyct2zTrmz1OdY6nlVfgxKrE3ohD3fPMYDEanJ2u9SFWIDvby0ek5zYPEWsLs1QenmXp5shW+gqdLzAegSwSaC+gqdpLY8Gs6xDuIPBOzRwP68KiTwuxNJt7wswt88Hpr8rGuZhQuOTd0jgOtGCqv1Pc4rM1ndYUlwzmQg= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1781870204; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=gqIwZ7pDS0ClXBCSxgyhjiGU3E85OgOvkzaZD9oYhGk=; b=E2rBa/yonRkLKMykLJMXHUXlDF4uowmaWryR5W7WKmivo/C2T+w8W3WkQkqS8wU4uT8s2aGMgiFFaToAfakJ2OctyBnFTXaVywmnq+2Seg739HrlhthTgxvmRHvuEufM3tJ8Qyo11ASq3sCjPn+kEAlnmtgruigeILg4ArkMex0= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists1p.gnu.org (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1781870204587365.07415575883874; Fri, 19 Jun 2026 04:56:44 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1waXpO-0003uF-82; Fri, 19 Jun 2026 07:56:06 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists1p.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1waXpK-0003rk-RK for qemu-devel@nongnu.org; Fri, 19 Jun 2026 07:56:02 -0400 Received: from mail-wm1-x334.google.com ([2a00:1450:4864:20::334]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1waXpF-0007Pj-HT for qemu-devel@nongnu.org; Fri, 19 Jun 2026 07:56:02 -0400 Received: by mail-wm1-x334.google.com with SMTP id 5b1f17b1804b1-490b1bbcf3aso13195445e9.1 for ; Fri, 19 Jun 2026 04:55:57 -0700 (PDT) Received: from localhost.localdomain ([188.111.3.154]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-4923ff821aasm64439975e9.12.2026.06.19.04.55.54 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 19 Jun 2026 04:55:55 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=9elements.com; s=google; t=1781870156; x=1782474956; darn=nongnu.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=gqIwZ7pDS0ClXBCSxgyhjiGU3E85OgOvkzaZD9oYhGk=; b=flxHELkcfwvatcB11RQ2l8DXN6RyrPgXTLtnlctRSJPUKAEAw9dAUjfso1LL4ifWqe LKpZy7hzHtkkImgM7r9II2DFrnYICw+qq0C4t67HOvWMG7CngP9b9pXuytspinRAxCDy auh77sVXxHiGXZTiGLDiwkZZwvTfyZzbFn/tnMDsp+/wRDn7xODc4BkLI+FuI+RQ11Xj GqhDa94rWcaiO4HQ/A4epsRe52q7IXSfQMg2uFuu3cR/POCN5Mu13nZOBUtEWDFS11bw ZwVHsXaF5PYeqNbdgXtx6NUKFiZqvMDb3TvON1mpJucIoLOn21t3X1FfFCBY+Yo3gxs0 5ycQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1781870156; x=1782474956; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=gqIwZ7pDS0ClXBCSxgyhjiGU3E85OgOvkzaZD9oYhGk=; b=l+n4R50Fn5/Z/8ukoEs/dZS4waYcenhaZ3Db7Q1ojAZ2rJ9M75DT8xAhQl2v+rR97Q RKEq8toqKbau3MekxB0KU+jzd3gCKaADxwpzU/MeVkKktHB4jp3FtZ9OAWBNIUO9FcCq vBv8J7fsMYzNO2u9apLsBTqrBlJV4SNEjLPbMalWI7aJNhV6dHhfCBpk8+J7wSgj5u4q 0YmrIHblVvNKwa+uNt5MpGIUaQdjGQQc7eeZCsZQCs7vx/13MympTaVOxQkRQKRGwkH3 R81W+/7qBzDb28m74iILyH8RHLzGQNoOl1NlkWsgr/UPTTjKc8bnHjHXifVEsv5QWCf9 6oUQ== X-Gm-Message-State: AOJu0YwvqmPaborw/UplMXhVZ1zsxu9uwg8Vi4OTQy7YqWfh6Zi77IPW l4qMnPybVT71pNloYFObXK4kTganM6NZUeoBB6HZIJvuBF0r7W6Tg6PPaAfqFZfYqseghCXHHnj KgaoLVDY= X-Gm-Gg: AfdE7cnbXUuT3lVOsaL9BY4PJUD8Uqc57Z9zrwmrbJ2R5TZC9RS5Rp60/+w5xZquFmS sKU3zWh8lAvbtAlHr8zH6kNx4uwYHzAT1fMMdyK4UElxaBcaw//52BBwu6Zx9jjsCBlhDzyguDB IlNVJv7a/Zio5fRv2TTIaQMAH/iBfFnvc3W9JVFfBLqG4GxiETwkl4xCMc96aKKPs0r7WO73oVm 3hFdg+14C+96LYC+93fo8UvURqzUiGe+cDEkrjh4Eifk4DMRHAYyCol8ti7up8Ut7WFRzndhkxX GcqQustUrErmgsOkWOz6QwEDG9vJLbBOdike7dFXfBeBOyk/ZpA1LUFGIS5/Bb6FYxHJEj2GyGG bukmXe+I7HmjZcJP/TjKCDo2uAX2k5F+oIgegRax/0Pr72BGzr4GftxinN8oOlArNEb7tVZ/nYz YNCm6zv0pDpzWLD8gpg6CZVpE7+/twuDbSPUdcaum7IPSg0GcJ X-Received: by 2002:a05:600c:8485:b0:492:3d74:63ea with SMTP id 5b1f17b1804b1-4923f571ad4mr69809225e9.18.1781870155923; Fri, 19 Jun 2026 04:55:55 -0700 (PDT) From: To: qemu-devel@nongnu.org Cc: eblot@meta.com, titusr@google.com, clg@kaod.org, Alexander Hansen , Jonathan Cameron , Paolo Bonzini , Peter Maydell , =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= , qemu-arm@nongnu.org Subject: [PATCH v7 3/4] hw/sensor: MAX31790 support Date: Fri, 19 Jun 2026 13:55:32 +0200 Message-ID: <20260619115533.5664-4-alexander.hansen@9elements.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260619115533.5664-1-alexander.hansen@9elements.com> References: <20260619115533.5664-1-alexander.hansen@9elements.com> MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists1p.gnu.org; Received-SPF: pass client-ip=2a00:1450:4864:20::334; envelope-from=alexander.hansen@9elements.com; helo=mail-wm1-x334.google.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=unavailable autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @9elements.com) X-ZM-MESSAGEID: 1781870206445158500 From: Alexander Hansen Product: [1] Datasheet: [2] MAX31790 Support: - fan inputs are reading - tach reading propertional to pwm setting from linux driver - fans do not show any fault - 6 PWM registers influence 6 TACH registers There is intentional stub behavior in some places and various functions of the device are currently unsupported. MAX31790 currently unsupported: - slave address restriction - fan dynamics - spin-up configuration - fault state / failure possibility - rate-of-change control - tach mode - mixed layouts where number of fans !=3D number of tachs - see Figure 5.9 in [2] for example of mixed layout Anyone could expand it in the future for more accurate emulation. The reason for adding this device is to support Yosemite V4 emulation. Tested: on yosemite 4 qemu root@yosemite4:~# ls /sys/class/hwmon/hwmon2/ device fan2_fault fan3_target fan5_fault fan6_target pwm2 pw= m5 fan1_enable fan2_input fan4_enable fan5_input name pwm2_enable pwm5_= enable fan1_fault fan2_target fan4_fault fan5_target of_node pwm3 pwm6 fan1_input fan3_enable fan4_input fan6_enable power pwm3_enable pwm6= _enable fan1_target fan3_fault fan4_target fan6_fault pwm1 pwm4 subsystem fan2_enable fan3_input fan5_enable fan6_input pwm1_enable pwm4_enabl= e uevent root@yosemite4:~# cat /sys/class/hwmon/hwmon2/fan1_input 4551 root@yosemite4:~# cat /sys/class/hwmon/hwmon2/fan1_enable 1 root@yosemite4:~# cat /sys/class/hwmon/hwmon2/fan1_fault 0 root@yosemite4:~# cat /sys/class/hwmon/hwmon2/fan1_target 2048 root@yosemite4:~# cat /sys/class/hwmon/hwmon2/pwm1 178 root@yosemite4:~# cat /sys/class/hwmon/hwmon2/name max31790 Trace output: max31790_realize i2c_addr: 0x20 max31790_realize i2c_addr: 0x2f max31790_realize i2c_addr: 0x20 max31790_realize i2c_addr: 0x2f max31790_event i2c_addr: 0x20, event: 0x01 max31790_send i2c_addr: 0x20, data: 0x02 max31790_event i2c_addr: 0x20, event: 0x00 max31790_recv i2c_addr: 0x20, reg_addr: 0x02 max31790_recv_return i2c_addr: 0x20, returns: 0x08 ... References: [1] https://www.analog.com/en/products/MAX31790.html [2] https://www.analog.com/media/en/technical-documentation/data-sheets/MAX= 31790.pdf Cc: Emmanuel Blot Cc: Titus Rwantare Cc: "C=C3=A9dric Le Goater" (maintainer:ASPEED BMCs) Cc: Jonathan Cameron Cc: Paolo Bonzini Cc: Peter Maydell Cc: "Philippe Mathieu-Daud=C3=A9" Cc: qemu-arm@nongnu.org Cc: qemu-devel@nongnu.org Signed-off-by: Alexander Hansen --- MAINTAINERS | 1 + hw/arm/Kconfig | 1 + hw/arm/aspeed_ast2600_fby4.c | 4 +- hw/sensor/Kconfig | 4 + hw/sensor/max31790.c | 584 +++++++++++++++++++++++ hw/sensor/meson.build | 1 + hw/sensor/trace-events | 13 + tests/functional/arm/test_aspeed_fby4.py | 47 ++ 8 files changed, 653 insertions(+), 2 deletions(-) create mode 100644 hw/sensor/max31790.c diff --git a/MAINTAINERS b/MAINTAINERS index 93df53d87f..e3df75f7bb 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3993,6 +3993,7 @@ S: Maintained F: hw/i2c/pmbus_device.c F: hw/sensor/adm1272.c F: hw/sensor/isl_pmbus_vr.c +F: hw/sensor/max31790.c F: hw/sensor/max34451.c F: include/hw/i2c/pmbus_device.h F: include/hw/sensor/isl_pmbus_vr.h diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index 500bfdfe2a..43e96a671b 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -553,6 +553,7 @@ config ASPEED_SOC select LED select PMBUS select MAX31785 + select MAX31790 select FSI_APB2OPB_ASPEED select AT24C select PCI_EXPRESS diff --git a/hw/arm/aspeed_ast2600_fby4.c b/hw/arm/aspeed_ast2600_fby4.c index 03f54d4393..8feb31bc44 100644 --- a/hw/arm/aspeed_ast2600_fby4.c +++ b/hw/arm/aspeed_ast2600_fby4.c @@ -164,7 +164,7 @@ static void fby4_i2c_init_fanboard(I2CSlave *fan_mux, s= ize_t eepromSize) /* TODO */ =20 /* maxim,max31790 @ 0x20 (pwm) */ - /* TODO */ + i2c_slave_create_simple(bus, "max31790", 0x20); =20 /* * ti,tca6424 @ 0x22 (gpio) @@ -182,7 +182,7 @@ static void fby4_i2c_init_fanboard(I2CSlave *fan_mux, s= ize_t eepromSize) */ =20 /* maxim,max31790 @ 0x2f (pwm) */ - /* TODO */ + i2c_slave_create_simple(bus, "max31790", 0x2f); =20 /* maxim,max11615 @ 0x33 (adc) */ /* TODO */ diff --git a/hw/sensor/Kconfig b/hw/sensor/Kconfig index bc6331b4ab..767f198e92 100644 --- a/hw/sensor/Kconfig +++ b/hw/sensor/Kconfig @@ -43,3 +43,7 @@ config ISL_PMBUS_VR config MAX31785 bool depends on PMBUS + +config MAX31790 + bool + depends on I2C diff --git a/hw/sensor/max31790.c b/hw/sensor/max31790.c new file mode 100644 index 0000000000..af610376d1 --- /dev/null +++ b/hw/sensor/max31790.c @@ -0,0 +1,584 @@ +/* + * Maxim MAX31790 PMBus 6-Channel Fan Controller + * + * Datasheet: + * https://www.analog.com/media/en/technical-documentation/data-sheets/MAX= 31790.pdf + * + * Copyright 2026 9elements + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "hw/i2c/i2c.h" +#include "qapi/error.h" +#include "qapi/visitor.h" +#include "migration/vmstate.h" +#include "qemu/module.h" +#include "qom/object.h" +#include "trace.h" + +#define TYPE_MAX31790 "max31790" + +#define MAX31790_NUM_FANS 6 +#define MAX31790_NUM_TACHS 12 + +#define MAX31790_REG_GLOBAL_CONFIG 0x00 +#define MAX31790_REG_PWM_FREQ 0x01 + +/* 0x02 to 0x07: N =3D 0 .. 5 */ +#define MAX31790_REG_FAN_CONFIG(N) (0x02 + (N)) + +/* 0x08 to 0x0d: N =3D 0 .. 5 */ +#define MAX31790_REG_FAN_DYNAMICS(N) (0x08 + (N)) + +#define MAX31790_REG_FAN_FAULT_STATUS_2 0x10 +#define MAX31790_REG_FAN_FAULT_STATUS_1 0x11 +#define MAX31790_REG_FAN_FAULT_MASK_2 0x12 +#define MAX31790_REG_FAN_FAULT_MASK_1 0x13 +#define MAX31790_REG_FAILED_FAN_OPT 0x14 + +/* 0x18 to 0x2f: N =3D 0 .. 11 */ +#define MAX31790_REG_TACH_COUNT_MSB(N) (0x18 + 2 * (N)) +#define MAX31790_REG_TACH_COUNT_LSB(N) (0x19 + 2 * (N)) + +/* 0x30 to 0x3b: N =3D 0 .. 5 */ +#define MAX31790_REG_PWM_DUTY_CYCLE_MSB(N) (0x30 + 2 * (N)) +#define MAX31790_REG_PWM_DUTY_CYCLE_LSB(N) (0x31 + 2 * (N)) + +/* .. reserved registers ... */ + +/* 0x40 to 0x4b: N =3D 0 .. 5 */ +#define MAX31790_REG_PWM_TARGET_DUTY_CYCLE_MSB(N) (0x40 + 2 * (N)) +#define MAX31790_REG_PWM_TARGET_DUTY_CYCLE_LSB(N) (0x41 + 2 * (N)) + +/* ... 'User Byte' registers ... */ + +/* 0x50 to 0x5b: N =3D 0 .. 5 */ +#define MAX31790_REG_TACH_TARGET_COUNT_MSB(N) (0x50 + 2 * (N)) +#define MAX31790_REG_TACH_TARGET_COUNT_LSB(N) (0x51 + 2 * (N)) + +struct MAX31790State { + I2CSlave i2c; + + uint8_t fan_config[MAX31790_NUM_FANS]; + uint8_t fan_dynamics[MAX31790_NUM_FANS]; + + uint16_t pwm[MAX31790_NUM_FANS]; + uint16_t tach_target[MAX31790_NUM_FANS]; + + /* + * Fan speed is measured by counting the number of internal 8192Hz + * (fTOSC/4) clock cycles that take place during a select- + * able number of tachometer periods. The number of clock + * cycles counted (11-bit value) is stored in the associated + * TACH Count registers and the desired number of cycles + * is stored in the TACH Target Count registers. + */ + uint16_t tach_count[MAX31790_NUM_TACHS]; + + /* command buffer */ + uint8_t len; + uint8_t buf[2]; + + /* output buffer */ + uint8_t outlen; + uint8_t outbuf[2]; + + /* selected register for read/write operation */ + uint8_t pointer; +}; + +struct MAX31790Class { + I2CSlaveClass parent_class; +}; + +OBJECT_DECLARE_TYPE(MAX31790State, MAX31790Class, MAX31790) + +static void max31790_read(MAX31790State *s) +{ + size_t index =3D 0; + uint8_t out0 =3D 0; + uint8_t out1 =3D 0; + + switch (s->pointer) { + case MAX31790_REG_FAN_CONFIG(0)... MAX31790_REG_FAN_CONFIG(5): + index =3D s->pointer - MAX31790_REG_FAN_CONFIG(0); + + g_assert(index < MAX31790_NUM_FANS); + + out0 =3D s->fan_config[index]; + trace_max31790_fan_config_read(s->i2c.address, index, out0); + break; + case MAX31790_REG_FAN_DYNAMICS(0)... MAX31790_REG_FAN_DYNAMICS(5): + index =3D s->pointer - MAX31790_REG_FAN_DYNAMICS(0); + + g_assert(index < MAX31790_NUM_FANS); + + out0 =3D s->fan_dynamics[index]; + trace_max31790_fan_dynamics_read(s->i2c.address, index, out0); + break; + case MAX31790_REG_FAN_FAULT_STATUS_1: + case MAX31790_REG_FAN_FAULT_STATUS_2: + /* we do not have any fan fault */ + out0 =3D 0x00; + out1 =3D 0x00; + break; + case MAX31790_REG_TACH_COUNT_MSB(0): + case MAX31790_REG_TACH_COUNT_MSB(1): + case MAX31790_REG_TACH_COUNT_MSB(2): + case MAX31790_REG_TACH_COUNT_MSB(3): + case MAX31790_REG_TACH_COUNT_MSB(4): + case MAX31790_REG_TACH_COUNT_MSB(5): + case MAX31790_REG_TACH_COUNT_MSB(6): + case MAX31790_REG_TACH_COUNT_MSB(7): + case MAX31790_REG_TACH_COUNT_MSB(8): + case MAX31790_REG_TACH_COUNT_MSB(9): + case MAX31790_REG_TACH_COUNT_MSB(10): + case MAX31790_REG_TACH_COUNT_MSB(11): + index =3D (s->pointer - MAX31790_REG_TACH_COUNT_MSB(0)) / 2; + + g_assert(index < MAX31790_NUM_TACHS); + + out0 =3D (s->tach_count[index % MAX31790_NUM_TACHS] >> 8) & 0xff; + out1 =3D s->tach_count[index % MAX31790_NUM_TACHS] & 0xff; + break; + + case MAX31790_REG_TACH_COUNT_LSB(0): + case MAX31790_REG_TACH_COUNT_LSB(1): + case MAX31790_REG_TACH_COUNT_LSB(2): + case MAX31790_REG_TACH_COUNT_LSB(3): + case MAX31790_REG_TACH_COUNT_LSB(4): + case MAX31790_REG_TACH_COUNT_LSB(5): + case MAX31790_REG_TACH_COUNT_LSB(6): + case MAX31790_REG_TACH_COUNT_LSB(7): + case MAX31790_REG_TACH_COUNT_LSB(8): + case MAX31790_REG_TACH_COUNT_LSB(9): + case MAX31790_REG_TACH_COUNT_LSB(10): + case MAX31790_REG_TACH_COUNT_LSB(11): + index =3D (s->pointer - MAX31790_REG_TACH_COUNT_LSB(0)) / 2; + + g_assert(index < MAX31790_NUM_TACHS); + + out0 =3D s->tach_count[index % MAX31790_NUM_TACHS] & 0xff; + break; + + case MAX31790_REG_PWM_DUTY_CYCLE_MSB(0): + case MAX31790_REG_PWM_DUTY_CYCLE_MSB(1): + case MAX31790_REG_PWM_DUTY_CYCLE_MSB(2): + case MAX31790_REG_PWM_DUTY_CYCLE_MSB(3): + case MAX31790_REG_PWM_DUTY_CYCLE_MSB(4): + case MAX31790_REG_PWM_DUTY_CYCLE_MSB(5): + index =3D (s->pointer - MAX31790_REG_PWM_DUTY_CYCLE_MSB(0)) / 2; + + g_assert(index < MAX31790_NUM_FANS); + + out0 =3D (s->pwm[index % MAX31790_NUM_FANS] >> 8) & 0xff; + out1 =3D s->pwm[index % MAX31790_NUM_FANS] & 0xff; + break; + case MAX31790_REG_PWM_DUTY_CYCLE_LSB(0): + case MAX31790_REG_PWM_DUTY_CYCLE_LSB(1): + case MAX31790_REG_PWM_DUTY_CYCLE_LSB(2): + case MAX31790_REG_PWM_DUTY_CYCLE_LSB(3): + case MAX31790_REG_PWM_DUTY_CYCLE_LSB(4): + case MAX31790_REG_PWM_DUTY_CYCLE_LSB(5): + index =3D (s->pointer - MAX31790_REG_PWM_DUTY_CYCLE_LSB(0)) / 2; + + g_assert(index < MAX31790_NUM_FANS); + + out0 =3D s->pwm[index % MAX31790_NUM_FANS] & 0xff; + break; + + case MAX31790_REG_PWM_TARGET_DUTY_CYCLE_MSB(0): + case MAX31790_REG_PWM_TARGET_DUTY_CYCLE_MSB(1): + case MAX31790_REG_PWM_TARGET_DUTY_CYCLE_MSB(2): + case MAX31790_REG_PWM_TARGET_DUTY_CYCLE_MSB(3): + case MAX31790_REG_PWM_TARGET_DUTY_CYCLE_MSB(4): + case MAX31790_REG_PWM_TARGET_DUTY_CYCLE_MSB(5): + index =3D (s->pointer - MAX31790_REG_PWM_TARGET_DUTY_CYCLE_MSB(0))= / 2; + + g_assert(index < MAX31790_NUM_FANS); + + out0 =3D (s->pwm[index % MAX31790_NUM_FANS] >> 8) & 0xff; + out1 =3D s->pwm[index % MAX31790_NUM_FANS] & 0xff; + break; + case MAX31790_REG_PWM_TARGET_DUTY_CYCLE_LSB(0): + case MAX31790_REG_PWM_TARGET_DUTY_CYCLE_LSB(1): + case MAX31790_REG_PWM_TARGET_DUTY_CYCLE_LSB(2): + case MAX31790_REG_PWM_TARGET_DUTY_CYCLE_LSB(3): + case MAX31790_REG_PWM_TARGET_DUTY_CYCLE_LSB(4): + case MAX31790_REG_PWM_TARGET_DUTY_CYCLE_LSB(5): + index =3D (s->pointer - MAX31790_REG_PWM_TARGET_DUTY_CYCLE_LSB(0))= / 2; + + g_assert(index < MAX31790_NUM_FANS); + + out0 =3D s->pwm[index % MAX31790_NUM_FANS] & 0xff; + break; + case MAX31790_REG_TACH_TARGET_COUNT_MSB(0): + case MAX31790_REG_TACH_TARGET_COUNT_MSB(1): + case MAX31790_REG_TACH_TARGET_COUNT_MSB(2): + case MAX31790_REG_TACH_TARGET_COUNT_MSB(3): + case MAX31790_REG_TACH_TARGET_COUNT_MSB(4): + case MAX31790_REG_TACH_TARGET_COUNT_MSB(5): + index =3D (s->pointer - MAX31790_REG_TACH_TARGET_COUNT_MSB(0)) / 2; + + g_assert(index < MAX31790_NUM_FANS); + + out0 =3D (s->tach_target[index] >> 8) & 0xff; + out1 =3D s->tach_target[index] & 0xff; + break; + case MAX31790_REG_TACH_TARGET_COUNT_LSB(0): + case MAX31790_REG_TACH_TARGET_COUNT_LSB(1): + case MAX31790_REG_TACH_TARGET_COUNT_LSB(2): + case MAX31790_REG_TACH_TARGET_COUNT_LSB(3): + case MAX31790_REG_TACH_TARGET_COUNT_LSB(4): + case MAX31790_REG_TACH_TARGET_COUNT_LSB(5): + index =3D (s->pointer - MAX31790_REG_TACH_TARGET_COUNT_LSB(0)) / 2; + + g_assert(index < MAX31790_NUM_FANS); + + out0 =3D s->tach_target[index] & 0xff; + break; + default: + qemu_log_mask(LOG_UNIMP, "%s: read of register %d\n", __func__, + s->pointer); + break; + } + + s->outbuf[0] =3D out0; + s->outbuf[1] =3D out1; +} + +static uint8_t max31790_get_speed_range(MAX31790State *s, size_t index) +{ + g_assert(index < MAX31790_NUM_FANS); + const uint16_t value =3D (s->fan_dynamics[index] >> 5); + return (value > 5) ? 32 : 1 << value; +} + +static void max31790_set_rpm(MAX31790State *s, size_t index, uint16_t rpm) +{ + trace_max31790_rpm_write(s->i2c.address, index, rpm); + + /* + * (see page 11) NP =3D number of tachometer pulses per revolution. + * Most general-purpose brushless DC fans produce two tachometer + * pulses per revolution. + */ + const uint64_t sr =3D max31790_get_speed_range(s, index); + + /* page 11: tach_count =3D 60 * SR * 8192 / (NP * RPM) */ + + /* prevent division by 0 */ + rpm =3D (rpm =3D=3D 0) ? 1 : rpm; + + /* (60 * 8192) / 2 */ + const uint64_t tach_count_constant =3D 245760ULL; + + /* simplified, assuming NP =3D 2 */ + uint64_t tach_count =3D (tach_count_constant * sr) / (uint64_t)rpm; + + /* in case of overflow, clamp to the maximum value */ + if (tach_count > 0x7ff) { + tach_count =3D 0x7ff; + } + + g_assert(index < MAX31790_NUM_TACHS); + + /* datasheet: lowest 5 bits are 0 */ + s->tach_count[index] =3D (uint16_t)(tach_count << 5); + + trace_max31790_tach_count_reg(s->i2c.address, index, s->tach_count[ind= ex]); +} + +static void max31790_pwm_write(MAX31790State *s, size_t index, uint16_t va= lue) +{ + trace_max31790_pwm_write(s->i2c.address, index, value); + + g_assert(index < MAX31790_NUM_FANS); + + s->pwm[index % MAX31790_NUM_FANS] =3D value; + + /* last 7 bits in register are 0 , 9 bits remaining */ + const uint16_t pwm_real =3D s->pwm[index % MAX31790_NUM_FANS] >> 7; + + /* maximum value will be 511 */ + + /* + * This formula has magic values which model the relationship + * of PWM input to a fan. Not derived from datasheet. + */ + max31790_set_rpm(s, index, 100 + pwm_real * 20); +} + +static void max31790_write_2_byte(MAX31790State *s) +{ + size_t index =3D 0; + const uint8_t value0 =3D s->buf[0]; + const uint8_t value1 =3D s->buf[1]; + + switch (s->pointer) { + case MAX31790_REG_FAN_CONFIG(0)... MAX31790_REG_FAN_CONFIG(5): + break; /* handled by one byte write */ + case MAX31790_REG_FAN_DYNAMICS(0)... MAX31790_REG_FAN_DYNAMICS(5): + break; /* handled by one byte write */ + case MAX31790_REG_PWM_TARGET_DUTY_CYCLE_MSB(0): + case MAX31790_REG_PWM_TARGET_DUTY_CYCLE_MSB(1): + case MAX31790_REG_PWM_TARGET_DUTY_CYCLE_MSB(2): + case MAX31790_REG_PWM_TARGET_DUTY_CYCLE_MSB(3): + case MAX31790_REG_PWM_TARGET_DUTY_CYCLE_MSB(4): + case MAX31790_REG_PWM_TARGET_DUTY_CYCLE_MSB(5): + index =3D (s->pointer - MAX31790_REG_PWM_TARGET_DUTY_CYCLE_MSB(0))= / 2; + max31790_pwm_write(s, index, value0 << 8 | value1); + break; + case MAX31790_REG_TACH_TARGET_COUNT_MSB(0): + case MAX31790_REG_TACH_TARGET_COUNT_MSB(1): + case MAX31790_REG_TACH_TARGET_COUNT_MSB(2): + case MAX31790_REG_TACH_TARGET_COUNT_MSB(3): + case MAX31790_REG_TACH_TARGET_COUNT_MSB(4): + case MAX31790_REG_TACH_TARGET_COUNT_MSB(5): + index =3D (s->pointer - MAX31790_REG_TACH_TARGET_COUNT_MSB(0)) / 2; + + g_assert(index < MAX31790_NUM_FANS); + + s->tach_target[index] =3D (value0 << 8) | value1; + break; + default: + qemu_log_mask(LOG_UNIMP, "%s: write to register %d\n", __func__, + s->pointer); + break; + } +} + +static void max31790_write_1_byte(MAX31790State *s) +{ + size_t index =3D 0; + uint16_t pwm =3D 0; + const uint8_t value =3D s->buf[0]; + + switch (s->pointer) { + case MAX31790_REG_FAN_CONFIG(0)... MAX31790_REG_FAN_CONFIG(5): + index =3D s->pointer - MAX31790_REG_FAN_CONFIG(0); + + g_assert(index < MAX31790_NUM_FANS); + + s->fan_config[index] =3D value; + trace_max31790_fan_config_write(s->i2c.address, index, value); + break; + case MAX31790_REG_FAN_DYNAMICS(0)... MAX31790_REG_FAN_DYNAMICS(5): + index =3D s->pointer - MAX31790_REG_FAN_DYNAMICS(0); + + g_assert(index < MAX31790_NUM_FANS); + + s->fan_dynamics[index] =3D value; + trace_max31790_fan_dynamics_write(s->i2c.address, index, value); + break; + case MAX31790_REG_PWM_TARGET_DUTY_CYCLE_MSB(0): + case MAX31790_REG_PWM_TARGET_DUTY_CYCLE_MSB(1): + case MAX31790_REG_PWM_TARGET_DUTY_CYCLE_MSB(2): + case MAX31790_REG_PWM_TARGET_DUTY_CYCLE_MSB(3): + case MAX31790_REG_PWM_TARGET_DUTY_CYCLE_MSB(4): + case MAX31790_REG_PWM_TARGET_DUTY_CYCLE_MSB(5): + index =3D (s->pointer - MAX31790_REG_PWM_TARGET_DUTY_CYCLE_MSB(0))= / 2; + + g_assert(index < MAX31790_NUM_FANS); + + pwm =3D (value << 8) | (s->pwm[index % MAX31790_NUM_FANS] & 0x00ff= ); + max31790_pwm_write(s, index, pwm); + break; + case MAX31790_REG_PWM_TARGET_DUTY_CYCLE_LSB(0): + case MAX31790_REG_PWM_TARGET_DUTY_CYCLE_LSB(1): + case MAX31790_REG_PWM_TARGET_DUTY_CYCLE_LSB(2): + case MAX31790_REG_PWM_TARGET_DUTY_CYCLE_LSB(3): + case MAX31790_REG_PWM_TARGET_DUTY_CYCLE_LSB(4): + case MAX31790_REG_PWM_TARGET_DUTY_CYCLE_LSB(5): + index =3D (s->pointer - MAX31790_REG_PWM_TARGET_DUTY_CYCLE_LSB(0))= / 2; + + g_assert(index < MAX31790_NUM_FANS); + + pwm =3D (s->pwm[index % MAX31790_NUM_FANS] & 0xff00) | (value & 0x= 00ff); + max31790_pwm_write(s, index, pwm); + break; + case MAX31790_REG_TACH_TARGET_COUNT_MSB(0): + case MAX31790_REG_TACH_TARGET_COUNT_MSB(1): + case MAX31790_REG_TACH_TARGET_COUNT_MSB(2): + case MAX31790_REG_TACH_TARGET_COUNT_MSB(3): + case MAX31790_REG_TACH_TARGET_COUNT_MSB(4): + case MAX31790_REG_TACH_TARGET_COUNT_MSB(5): + index =3D (s->pointer - MAX31790_REG_TACH_TARGET_COUNT_MSB(0)) / 2; + + g_assert(index < MAX31790_NUM_FANS); + + s->tach_target[index] =3D (s->tach_target[index] & 0x00ff) | (valu= e << 8); + break; + case MAX31790_REG_TACH_TARGET_COUNT_LSB(0): + case MAX31790_REG_TACH_TARGET_COUNT_LSB(1): + case MAX31790_REG_TACH_TARGET_COUNT_LSB(2): + case MAX31790_REG_TACH_TARGET_COUNT_LSB(3): + case MAX31790_REG_TACH_TARGET_COUNT_LSB(4): + case MAX31790_REG_TACH_TARGET_COUNT_LSB(5): + index =3D (s->pointer - MAX31790_REG_TACH_TARGET_COUNT_LSB(0)) / 2; + + g_assert(index < MAX31790_NUM_FANS); + + s->tach_target[index] =3D (s->tach_target[index] & 0xff00) | value; + break; + default: + qemu_log_mask(LOG_UNIMP, "%s: write to register %d\n", __func__, + s->pointer); + break; + } +} + +static int max31790_send(I2CSlave *i2c, uint8_t data) +{ + MAX31790State *s =3D MAX31790(i2c); + + trace_max31790_send(s->i2c.address, data); + + if (s->len =3D=3D 0) { + /* first byte is the register pointer for a read / write operation= */ + s->pointer =3D data; + s->len++; + return 0; + } + + if (s->len > 2) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: write too many bytes\n", __fun= c__); + return 1; /* NAK */ + } + + /* second / third byte is the data to write */ + s->buf[s->len - 1] =3D data; + s->len++; + + if (s->len =3D=3D 2) { + max31790_write_1_byte(s); + } else if (s->len =3D=3D 3) { + max31790_write_2_byte(s); + } + + return 0; +} + +static uint8_t max31790_recv(I2CSlave *i2c) +{ + MAX31790State *s =3D MAX31790(i2c); + + max31790_read(s); + s->len =3D 0; + + if (s->outlen >=3D 2) { + /* error */ + qemu_log_mask(LOG_GUEST_ERROR, "%s: guest over read\n", __func__); + s->outlen =3D 0; + } + + const uint8_t data =3D s->outbuf[s->outlen++]; + + trace_max31790_recv_return(s->i2c.address, s->pointer, data); + return data; +} + +static int max31790_event(I2CSlave *i2c, enum i2c_event event) +{ + MAX31790State *s =3D MAX31790(i2c); + + switch (event) { + case I2C_START_RECV: + s->outlen =3D 0; + break; + case I2C_START_SEND: + s->len =3D 0; + break; + default: + break; + } + + return 0; +} + +static const VMStateDescription vmstate_max31790 =3D { + .name =3D TYPE_MAX31790, + .version_id =3D 0, + .minimum_version_id =3D 0, + .fields =3D + (const VMStateField[]){ + VMSTATE_UINT8(len, MAX31790State), + VMSTATE_UINT8_ARRAY(fan_config, MAX31790State, MAX31790_NUM_FA= NS), + VMSTATE_UINT8_ARRAY(fan_dynamics, MAX31790State, MAX31790_NUM_= FANS), + VMSTATE_UINT16_ARRAY(pwm, MAX31790State, MAX31790_NUM_FANS), + VMSTATE_UINT16_ARRAY(tach_target, MAX31790State, MAX31790_NUM_= FANS), + VMSTATE_UINT16_ARRAY(tach_count, MAX31790State, MAX31790_NUM_T= ACHS), + VMSTATE_UINT8_ARRAY(buf, MAX31790State, 2), + VMSTATE_UINT8(outlen, MAX31790State), + VMSTATE_UINT8_ARRAY(outbuf, MAX31790State, 2), + VMSTATE_UINT8(pointer, MAX31790State), + VMSTATE_I2C_SLAVE(i2c, MAX31790State), VMSTATE_END_OF_LIST() } +}; + +static void max31790_reset_enter(MAX31790State *s) +{ + trace_max31790_reset(s->i2c.address); + + for (int i =3D 0; i < MAX31790_NUM_FANS; i++) { + /* POR-State 0b 0XX0 0000 */ + s->fan_config[i] =3D 0x0; + + /* same as POR-State */ + s->tach_target[i] =3D 0x3c00; + + /* same as POR-State */ + s->fan_dynamics[i] =3D 0x4c; + } + + /* this will set both pwm and tach count */ + for (int i =3D 0; i < MAX31790_NUM_FANS; i++) { + max31790_pwm_write(s, i, 0x4444); + } +} + +static void max31790_reset_hold(Object *obj, ResetType type) +{ + MAX31790State *s =3D MAX31790(obj); + + max31790_reset_enter(s); +} + +static void max31790_realize(DeviceState *dev, Error **errp) +{ + MAX31790State *s =3D MAX31790(dev); + + trace_max31790_realize(s->i2c.address); +} + +static void max31790_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc =3D DEVICE_CLASS(klass); + I2CSlaveClass *k =3D I2C_SLAVE_CLASS(klass); + ResettableClass *rc =3D RESETTABLE_CLASS(klass); + + dc->realize =3D max31790_realize; + dc->desc =3D "Maxim MAX31790 6-Channel Fan Controller"; + dc->vmsd =3D &vmstate_max31790; + rc->phases.hold =3D max31790_reset_hold; + k->event =3D max31790_event; + k->recv =3D max31790_recv; + k->send =3D max31790_send; +} + +static const TypeInfo max31790_info =3D { + .name =3D TYPE_MAX31790, + .parent =3D TYPE_I2C_SLAVE, + .instance_size =3D sizeof(MAX31790State), + .class_size =3D sizeof(MAX31790Class), + .class_init =3D max31790_class_init, +}; + +static void max31790_register_types(void) +{ + type_register_static(&max31790_info); +} + +type_init(max31790_register_types) diff --git a/hw/sensor/meson.build b/hw/sensor/meson.build index 420fdc3359..4987c3b253 100644 --- a/hw/sensor/meson.build +++ b/hw/sensor/meson.build @@ -8,3 +8,4 @@ system_ss.add(when: 'CONFIG_MAX34451', if_true: files('max3= 4451.c')) system_ss.add(when: 'CONFIG_LSM303DLHC_MAG', if_true: files('lsm303dlhc_ma= g.c')) system_ss.add(when: 'CONFIG_ISL_PMBUS_VR', if_true: files('isl_pmbus_vr.c'= )) system_ss.add(when: 'CONFIG_MAX31785', if_true: files('max31785.c')) +system_ss.add(when: 'CONFIG_MAX31790', if_true: files('max31790.c')) diff --git a/hw/sensor/trace-events b/hw/sensor/trace-events index a3fe54fa6d..35c30b985c 100644 --- a/hw/sensor/trace-events +++ b/hw/sensor/trace-events @@ -4,3 +4,16 @@ tmp105_read(uint8_t dev, uint8_t addr) "device: 0x%02x, addr: 0x%02x" tmp105_write(uint8_t dev, uint8_t addr) "device: 0x%02x, addr 0x%02x" tmp105_write_shutdown(uint8_t dev) "device: 0x%02x" + +# max31790.c +max31790_send(uint8_t i2c_addr, uint8_t send) "i2c_addr: 0x%02x, data: 0x%= 02x" +max31790_recv_return(uint8_t i2c_addr, uint8_t reg_addr, uint8_t data) "i2= c_addr: 0x%02x, reg_addr: 0x%02x, returns: 0x%02x" +max31790_realize(uint8_t i2c_addr) "i2c_addr: 0x%02x" +max31790_reset(uint8_t i2c_addr) "i2c_addr: 0x%02x" +max31790_pwm_write(uint8_t i2c_addr, size_t index, uint16_t value) "i2c_ad= dr: 0x%02x, index: %zu, value: 0x%04x" +max31790_rpm_write(uint8_t i2c_addr, size_t index, uint16_t value) "i2c_ad= dr: 0x%02x, index: %zu, value: 0x%04x" +max31790_tach_count_reg(uint8_t i2c_addr, size_t index, uint16_t value) "i= 2c_addr: 0x%02x, index: %zu, value: 0x%04x" +max31790_fan_dynamics_read(uint8_t i2c_addr, size_t channel, uint8_t value= ) "i2c_addr: 0x%02x, channel: %zu, value: 0x%02x" +max31790_fan_dynamics_write(uint8_t i2c_addr, size_t channel, uint8_t valu= e) "i2c_addr: 0x%02x, channel: %zu, value: 0x%02x" +max31790_fan_config_read(uint8_t i2c_addr, size_t channel, uint8_t value) = "i2c_addr: 0x%02x, channel: %zu, value: 0x%02x" +max31790_fan_config_write(uint8_t i2c_addr, size_t channel, uint8_t value)= "i2c_addr: 0x%02x, channel: %zu, value: 0x%02x" diff --git a/tests/functional/arm/test_aspeed_fby4.py b/tests/functional/ar= m/test_aspeed_fby4.py index a521834f79..f344413fe4 100755 --- a/tests/functional/arm/test_aspeed_fby4.py +++ b/tests/functional/arm/test_aspeed_fby4.py @@ -7,6 +7,7 @@ from qemu_test import Asset from qemu_test import wait_for_console_pattern from aspeed import AspeedTest +from qemu_test import exec_command_and_wait_for_pattern =20 =20 class YosemiteV4Machine(AspeedTest): @@ -34,6 +35,52 @@ def do_test_arm_aspeed_openbmc_no_network(self, machine,= image, uboot, # different from the other machines. self.wait_for_console_pattern('yosemite4 login:') =20 + # perform login + exec_command_and_wait_for_pattern(self, + "root", "Password:"); + + exec_command_and_wait_for_pattern(self, "0penBmc", "#"); + + # MAX31790 test + exec_command_and_wait_for_pattern(self, + "cat /sys/class/hwmon/hwmon2/name", "max31790"); + + # this initial fan reading value is caused by services + # inside the image writing the pwm + exec_command_and_wait_for_pattern(self, + "cat /sys/class/hwmon/hwmon2/fan1_input", "7281"); + exec_command_and_wait_for_pattern(self, + "cat /sys/class/hwmon/hwmon2/fan1_enable", "1"); + exec_command_and_wait_for_pattern(self, + "cat /sys/class/hwmon/hwmon2/fan1_fault", "0"); + + exec_command_and_wait_for_pattern(self, + "echo 1 > /sys/class/hwmon/hwmon2/pwm1", "root@yosemite4"); + exec_command_and_wait_for_pattern(self, + "cat /sys/class/hwmon/hwmon2/fan1_input", "0"); + + # linux driver pwm write: + # >>> hex(int((11 * 511) / 255) << 7) + # '0xb00' + exec_command_and_wait_for_pattern(self, + "echo 11 > /sys/class/hwmon/hwmon2/pwm1", "root@yosemite4"); + # device emulation logic: + # >>> hex(100 + (0xb00 >> 7) * 20) + # '0x21c' + # register reading value: + # >>> hex(int((245760 * 4) / 0x21c) << 5) + # '0xe380' + # linux driver calculation: + # >>> int((60 * 4 * 8192) / (0xe380 >> 4)) + # 540 + exec_command_and_wait_for_pattern(self, + "cat /sys/class/hwmon/hwmon2/fan1_input", "540"); + + exec_command_and_wait_for_pattern(self, + "echo 255 > /sys/class/hwmon/hwmon2/pwm1", "root@yosemite4"); + exec_command_and_wait_for_pattern(self, + "cat /sys/class/hwmon/hwmon2/fan1_input", "10347"); + def test_arm_ast2600_yosemitev4_openbmc(self): image_path =3D self.uncompress(self.ASSET_YOSEMITE_V4_FLASH) =20 --=20 2.54.0 From nobody Thu Jun 25 05:53:05 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=quarantine dis=none) header.from=9elements.com ARC-Seal: i=1; a=rsa-sha256; t=1781870210; cv=none; d=zohomail.com; s=zohoarc; b=RKjJfCd0CAHfxqxNYR+dQa+gPwIokC8uAj269Qa6Ank+Cq7LJY/Rots3JOy3ObjXU6Hc8z/U0pZ1Dy2z3IhGS3fsOyexO8Zs22Ye9uNPjWIBnwGyifN/SwLIniTK5416IeffR0pR4/PbfVeyecTmdrbORSTKtC0IWvAgGZmCrEc= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1781870210; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=PS/TuhXlvJSi58VgikiiV4TNJ+qf4mDFo/4hF9rtwac=; b=CdScFj3Ly/XC0CWLJAKZ7POSkN/6rxlBTxx/MMOIwpyTEfMqmqvIMZQzEk0qwJuvewm6apE8LlX9ihgM0BRryDFPDdRmNGauMBjC2mfdpYVnTR0STWM29ETCsflRrSgCi6DSqjNlnN967WUQuCKFtja6STFeSbQkTfcPv+0ncBc= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists1p.gnu.org (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1781870210770491.4216528644737; Fri, 19 Jun 2026 04:56:50 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1waXpN-0003u5-6a; Fri, 19 Jun 2026 07:56:05 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists1p.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1waXpL-0003sQ-Gu for qemu-devel@nongnu.org; Fri, 19 Jun 2026 07:56:03 -0400 Received: from mail-wm1-x331.google.com ([2a00:1450:4864:20::331]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1waXpH-0007QI-G6 for qemu-devel@nongnu.org; Fri, 19 Jun 2026 07:56:03 -0400 Received: by mail-wm1-x331.google.com with SMTP id 5b1f17b1804b1-490b64c8311so18905415e9.3 for ; Fri, 19 Jun 2026 04:55:59 -0700 (PDT) Received: from localhost.localdomain ([188.111.3.154]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-4923ff821aasm64439975e9.12.2026.06.19.04.55.57 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 19 Jun 2026 04:55:57 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=9elements.com; s=google; t=1781870158; x=1782474958; darn=nongnu.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=PS/TuhXlvJSi58VgikiiV4TNJ+qf4mDFo/4hF9rtwac=; b=QWoAT/19tmxqkI6K+hLinSjwDyl0lwWdg9aVhEIkuiiUm/IIxlV03D7xgyCUWMaRQQ f+IWQdwpZRQl97RgviAWfBTI6CSoQ/otmmNLta+duA5L1F2906QoZef2klJCU0nDmoZv x0e6eimzqESiOUL0p8gEt0jwMHFeQXrqIwZB6n6GtGizXRjWi/6C4T6pv7awU+wI/BFN VfECUKno1T7cvv7cRFuzjxFRylBHPw4q9xq2LDljD6R/bVdMVtOu47080F4iiQj5HTHY tyxFUUrSgI4ec/lGsC0EfXAJb+v8RjFAaKbP1r9tJgzbx7Ep8XmLH01pC7Dl+UUnQ8Ji 6B5A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1781870158; x=1782474958; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=PS/TuhXlvJSi58VgikiiV4TNJ+qf4mDFo/4hF9rtwac=; b=UrcFXS43QRbn0t78ueL5pBmW4O1aCKrWkQfQnPATAo/kAZ1TN1b4MuRQo/D+c4Zgkx ivCxaZnhJ4LLxmv3PY4NXbCTQr/89rg0rhLShxI9sgCwMp+UQMjn0N4sqp6F+3leslXj aKP9Meee/vBa2VsQxpYcXXrw0YmdE6imD13tnUnXUaqyNKX7+Fm47Vvnp0G5As6d9O6I BmBkEggGv64KxbuLBHOviiugPJnCmv9wcvASqXTas2Qi8wANS/uwjv7oPCqeAW4dd8zy jJvMcwDtRRKqN87KG5jx5leJZj6GN+agyYkAGXqVmoNEDnI4fCJDZtuaTpgyq83Cl8Wa VtxQ== X-Gm-Message-State: AOJu0YwUsdxlw3w0jr0el3L/4xBEy57NWQjLqsDfpq4Wecrw5cMougc8 lXrj+nGFHaxqvi4UDpV+4e0CKE+amskKKMDT2R35gfrliB8jYNG7IWe0ieTIxhXjpVOwLiBgfjT whoeeawU= X-Gm-Gg: AfdE7ck1yLA6UGKOYMrd40BaIEwcymwlOjXicZJLdJsn7C21MQ4mhg9sszcst3mmjzr ZtLDESYR4Y7HIKpykMeZG4Qpu9GPMcV8GJGa6X+FgDe5FOZzLA1eL7C8ti6d2UQn5f+arUZN/m/ B8wIdUjglN8e3cjw84qpkrpaNSUxBXhl9QrCJkFZlYhIQ1xEPwIbK/DSLV+y+rUd9vxosC0XELa 48WlEks40L/U1NAr3smKS1KIGJQHqx/stj/PdNL8rzRavSThdXmE5nhbDIgZ9dWOgWiEtXUZ5qW UWUjsqBOwA5QkyzrmNKR8zmUA1ocJt/h9ReaTfM+fHVShs86N2qgh1mTszs1BjEDae5QH1+8hnM PqUKRzSQE2uHngkS+1UpbjJDfPPMARUe45naZy5v8FzL/QW6bBglwqqQpDjx5vfN9HGKWqpVwJl 2wri+c2tJPbaO2dz8qL/Fhh8SJxV04QtU7IMCFSA== X-Received: by 2002:a05:600c:e555:10b0:490:e5c1:b8c6 with SMTP id 5b1f17b1804b1-49240dfd165mr37917745e9.8.1781870158034; Fri, 19 Jun 2026 04:55:58 -0700 (PDT) From: To: qemu-devel@nongnu.org Cc: eblot@meta.com, titusr@google.com, clg@kaod.org, Alexander Hansen , Jonathan Cameron , =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= , Paolo Bonzini , Peter Maydell , qemu-arm@nongnu.org Subject: [PATCH v7 4/4] hw/sensor: support MAX11615 Date: Fri, 19 Jun 2026 13:55:33 +0200 Message-ID: <20260619115533.5664-5-alexander.hansen@9elements.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260619115533.5664-1-alexander.hansen@9elements.com> References: <20260619115533.5664-1-alexander.hansen@9elements.com> MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists1p.gnu.org; Received-SPF: pass client-ip=2a00:1450:4864:20::331; envelope-from=alexander.hansen@9elements.com; helo=mail-wm1-x331.google.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=unavailable autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @9elements.com) X-ZM-MESSAGEID: 1781870212269158500 From: Alexander Hansen Product: [1] Datasheet: [2] Sensor readings can be provided upon creation of the device. In case no readings are provided the ADC reads a pre-defined arbitrary value. root@yosemite4:~# cat /sys/bus/iio/devices/iio:device2/name max11615 root@yosemite4:~# cat /sys/bus/iio/devices/iio:device2/in_voltage0_raw 1922 root@yosemite4:~# cat /sys/bus/iio/devices/iio:device2/in_voltage_scale 0.500000000 trace: less /tmp/qemu-trace.log | grep -i max116 max11615_realize i2c_addr: 0x33 max11615_realize i2c_addr: 0x33 max11615_event i2c_addr: 0x33, event: 0x01 max11615_write_setup i2c_addr: 0x33, data: 0xd2 max11615_write_config i2c_addr: 0x33, data: 0x0f max11615_event i2c_addr: 0x33, event: 0x03 max11615_event i2c_addr: 0x33, event: 0x01 max11615_write_setup i2c_addr: 0x33, data: 0xd2 max11615_write_config i2c_addr: 0x33, data: 0x0f max11615_event i2c_addr: 0x33, event: 0x03 max11615_event i2c_addr: 0x33, event: 0x01 max11615_write_setup i2c_addr: 0x33, data: 0xd2 max11615_write_config i2c_addr: 0x33, data: 0x61 max11615_event i2c_addr: 0x33, event: 0x03 max11615_event i2c_addr: 0x33, event: 0x00 max11615_recv i2c_addr: 0x33, reg_addr: 0x00 max11615_recv_return i2c_addr: 0x33, returns: 0xfa max11615_recv i2c_addr: 0x33, reg_addr: 0x00 max11615_recv_return i2c_addr: 0x33, returns: 0xd2 max11615_event i2c_addr: 0x33, event: 0x04 max11615_event i2c_addr: 0x33, event: 0x03 References: [1] https://www.analog.com/en/products/MAX11615.html [2] https://www.analog.com/media/en/technical-documentation/data-sheets/MAX= 11612-MAX11617.pdf Cc: Emmanuel Blot Cc: Titus Rwantare Cc: "C=C3=A9dric Le Goater" (maintainer:ASPEED BMCs) Cc: Jonathan Cameron Cc: "Philippe Mathieu-Daud=C3=A9" (odd fixer:Overall se= nsors) Cc: Paolo Bonzini (maintainer:Kconfig) Cc: Peter Maydell (supporter:ARM TCG CPUs) Cc: qemu-devel@nongnu.org (open list:All patches CC here) Cc: qemu-arm@nongnu.org (open list:ARM TCG CPUs) Signed-off-by: Alexander Hansen --- MAINTAINERS | 1 + hw/arm/Kconfig | 1 + hw/arm/aspeed_ast2600_fby4.c | 8 +- hw/sensor/Kconfig | 4 + hw/sensor/max11615.c | 320 +++++++++++++++++++++++ hw/sensor/meson.build | 1 + hw/sensor/trace-events | 9 + include/hw/sensor/max11615.h | 19 ++ tests/functional/arm/test_aspeed_fby4.py | 9 + 9 files changed, 371 insertions(+), 1 deletion(-) create mode 100644 hw/sensor/max11615.c create mode 100644 include/hw/sensor/max11615.h diff --git a/MAINTAINERS b/MAINTAINERS index e3df75f7bb..4b5c8cc65a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3993,6 +3993,7 @@ S: Maintained F: hw/i2c/pmbus_device.c F: hw/sensor/adm1272.c F: hw/sensor/isl_pmbus_vr.c +F: hw/sensor/max11615.c F: hw/sensor/max31790.c F: hw/sensor/max34451.c F: include/hw/i2c/pmbus_device.h diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index 43e96a671b..19259bc2e2 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -554,6 +554,7 @@ config ASPEED_SOC select PMBUS select MAX31785 select MAX31790 + select MAX11615 select FSI_APB2OPB_ASPEED select AT24C select PCI_EXPRESS diff --git a/hw/arm/aspeed_ast2600_fby4.c b/hw/arm/aspeed_ast2600_fby4.c index 8feb31bc44..dd84839c65 100644 --- a/hw/arm/aspeed_ast2600_fby4.c +++ b/hw/arm/aspeed_ast2600_fby4.c @@ -11,6 +11,7 @@ #include "hw/arm/aspeed.h" #include "hw/arm/aspeed_soc.h" #include "hw/nvram/eeprom_at24c.h" +#include "hw/sensor/max11615.h" #include "hw/i2c/i2c_mux_pca954x.h" #include "hw/gpio/pca9552.h" =20 @@ -185,7 +186,12 @@ static void fby4_i2c_init_fanboard(I2CSlave *fan_mux, = size_t eepromSize) i2c_slave_create_simple(bus, "max31790", 0x2f); =20 /* maxim,max11615 @ 0x33 (adc) */ - /* TODO */ + static const uint16_t adc_values[8] =3D { + 1922, 1000, + 1922, 1000, + 1922, 1000, + 1922, 1000}; + max11615_init_with_values(bus, 0x33, adc_values, 8); =20 at24c_eeprom_init_rom( bus, 0x52, eepromSize, diff --git a/hw/sensor/Kconfig b/hw/sensor/Kconfig index 767f198e92..441bee7604 100644 --- a/hw/sensor/Kconfig +++ b/hw/sensor/Kconfig @@ -47,3 +47,7 @@ config MAX31785 config MAX31790 bool depends on I2C + +config MAX11615 + bool + depends on I2C diff --git a/hw/sensor/max11615.c b/hw/sensor/max11615.c new file mode 100644 index 0000000000..ac2ccda4fa --- /dev/null +++ b/hw/sensor/max11615.c @@ -0,0 +1,320 @@ +/* + * Maxim MAX11615 Low-Power 12 bit ADC + * Models MAX11612,MAX11613,MAX11614,MAX11615,MAX11616,MAX11617 + * + * Datasheet: + * https://www.analog.com/media/en/technical-documentation/data-sheets/MAX= 11612-MAX11617.pdf + * + * Copyright 2026 9elements + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "hw/sensor/max11615.h" +#include "hw/i2c/i2c.h" +#include "migration/vmstate.h" +#include "qapi/error.h" +#include "qapi/visitor.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "trace.h" + +#define MAX11615_NUM_CHANNELS 8 + +struct MAX11615State { + I2CSlave i2c; + + /* true: single-ended mode, false: differential mode */ + bool single_ended; + + /* + * Output data coding for the MAX11612=E2=80=93MAX11617 is + * binary in unipolar mode and two=E2=80=99s complement in bipolar mode + */ + bool bipolar; + + /* The MAX11613/MAX11615/MAX11617 feature a 2.048V internal reference = */ + uint16_t vref; + + uint16_t channels[MAX11615_NUM_CHANNELS]; + + /* output buffer */ + uint8_t outlen; + uint8_t outbuf[2]; + + /* selected channel for read/write operation */ + uint8_t pointer; +}; + +struct MAX11615Class { + I2CSlaveClass parent_class; +}; + +OBJECT_DECLARE_TYPE(MAX11615State, MAX11615Class, MAX11615) + +static void max11615_set_outbuf(MAX11615State *s, uint16_t value) +{ + uint8_t msb =3D value >> 8; + uint8_t lsb =3D value & 0xff; + s->outbuf[0] =3D 0xf0 | (msb & 0xf); + s->outbuf[1] =3D lsb; +} + +static void max11615_read_single_ended(MAX11615State *s) +{ + /* Table 3. Channel Selection in Single-Ended Mode */ + /* read an ADC channel, first 4 bits must be high */ + uint16_t value =3D s->channels[s->pointer]; + /* + * In single-ended mode, the MAX11612=E2=80=93MAX11617 always + * operates in unipolar mode irrespective of BIP/UNI. + */ + if (value > s->vref) { + value =3D 0xfff; + } + max11615_set_outbuf(s, value); +} + +static int16_t max11615_differential_value(MAX11615State *s) +{ + /* Table 4. Channel Selection in Differential Mode */ + size_t i1 =3D s->pointer; + size_t i2 =3D (s->pointer % 2 =3D=3D 0) ? s->pointer + 1 : s->pointer = - 1; + + const int16_t ch1 =3D s->channels[i1]; + const int16_t ch2 =3D s->channels[i2]; + return ch1 - ch2; +} + +static void max11615_read_differential(MAX11615State *s) +{ + const int16_t value =3D max11615_differential_value(s); + uint16_t vu =3D value; + + /* full-scale transition: Figure 12, Figure 13 */ + if (s->bipolar) { + const uint16_t fs =3D s->vref / 2; + if (value < -fs) { + vu =3D 0x800; + } + if (value > fs) { + vu =3D 0x7ff; + } + } else { + /* + * A negative differential analog input in unipolar mode causes the + * digital output code to be zero + */ + if (value < 0) { + vu =3D 0; + } + } + + max11615_set_outbuf(s, vu); +} + +static void max11615_read(MAX11615State *s) +{ + if (s->single_ended) { + max11615_read_single_ended(s); + } else { + max11615_read_differential(s); + } +} + +static void max11615_write_config_byte(MAX11615State *s, uint8_t data) +{ + trace_max11615_write_config(s->i2c.address, data); + + uint8_t scan_select =3D (data >> 5) & 0x3; + + if (scan_select !=3D 0x3) { + qemu_log_mask(LOG_UNIMP, "%s: unimplemented scan select\n", __func= __); + } + + uint8_t channel_select =3D (data >> 1) & 0xf; + + /* Table 3. Channel Selection */ + if (channel_select >=3D MAX11615_NUM_CHANNELS) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid channel select\n", + __func__); + channel_select =3D MAX11615_NUM_CHANNELS - 1; + } + s->pointer =3D channel_select; + s->single_ended =3D data & 0x1; +} + +static void max11615_write_setup_byte(MAX11615State *s, uint8_t data) +{ + trace_max11615_write_setup(s->i2c.address, data); + /* we ignore the setup byte, not implemented */ + + /* 1 =3D no action, 0 =3D resets the configuration register to default= */ + const bool rst =3D ((data >> 1) & 0x1) =3D=3D 0x0; + + if (rst) { + s->single_ended =3D true; + s->pointer =3D 0; + } + + s->bipolar =3D ((data >> 2) & 0x1) =3D=3D 0x1; + + /* Table 6. Reference Voltage */ + const uint8_t sel =3D (data >> 4) & 0x7; + + if (sel =3D=3D 0x2 || sel =3D=3D 0x3) { + qemu_log_mask(LOG_UNIMP, "%s: unsupported: external vref\n", __fun= c__); + } +} + +static int max11615_send(I2CSlave *i2c, uint8_t data) +{ + MAX11615State *s =3D MAX11615(i2c); + const uint8_t msb =3D (data >> 7) & 0x1; + + if (msb) { + max11615_write_setup_byte(s, data); + } else { + max11615_write_config_byte(s, data); + } + + s->outlen =3D 0; + return 0; +} + +static uint8_t max11615_recv(I2CSlave *i2c) +{ + MAX11615State *s =3D MAX11615(i2c); + trace_max11615_recv(s->i2c.address, s->pointer); + + if (s->outlen >=3D 2) { + /* MAX11615 supports multichannel scan with wraparound */ + /* see datasheet page 17 */ + + s->outlen =3D 0; + + s->pointer++; + if (s->pointer >=3D MAX11615_NUM_CHANNELS) { + s->pointer =3D 0; + } + } + + max11615_read(s); + + const uint8_t data =3D s->outbuf[s->outlen++]; + + trace_max11615_recv_return(s->i2c.address, data); + return data; +} + +static int max11615_event(I2CSlave *i2c, enum i2c_event event) +{ + MAX11615State *s =3D MAX11615(i2c); + + trace_max11615_event(s->i2c.address, event); + + switch (event) { + case I2C_START_RECV: + s->outlen =3D 0; + break; + default: + break; + } + + return 0; +} + +static const VMStateDescription vmstate_max11615 =3D { + .name =3D TYPE_MAX11615, + .version_id =3D 0, + .minimum_version_id =3D 0, + .fields =3D + (const VMStateField[]){ VMSTATE_BOOL(single_ended, MAX11615State), + VMSTATE_BOOL(bipolar, MAX11615State), + VMSTATE_UINT16(vref, MAX11615State), + VMSTATE_UINT16_ARRAY(channels, MAX11615Sta= te, + MAX11615_NUM_CHANNELS= ), + VMSTATE_UINT8(outlen, MAX11615State), + VMSTATE_UINT8_ARRAY(outbuf, MAX11615State,= 2), + VMSTATE_UINT8(pointer, MAX11615State), + VMSTATE_I2C_SLAVE(i2c, MAX11615State), + VMSTATE_END_OF_LIST() } +}; + +I2CSlave *max11615_init_with_values(I2CBus *bus, uint8_t address, + const uint16_t *init_values, + uint32_t init_values_size) +{ + MAX11615State *s; + + s =3D MAX11615(i2c_slave_new(TYPE_MAX11615, address)); + + i2c_slave_realize_and_unref(I2C_SLAVE(s), bus, &error_abort); + + for (int i =3D 0; i < MAX11615_NUM_CHANNELS; i++) { + /* arbitrary value if there is no data*/ + s->channels[i] =3D i < init_values_size ? init_values[i] : 0x2d2; + } + + return I2C_SLAVE(s); +} + +static void max11615_reset_enter(MAX11615State *s) +{ + trace_max11615_reset_enter(s->i2c.address); + + s->single_ended =3D true; + s->bipolar =3D false; + s->vref =3D 2048; + s->pointer =3D 0; + s->outlen =3D 0; + + for (int i =3D 0; i < MAX11615_NUM_CHANNELS; i++) { + s->channels[i] =3D 0x2d2; + } +} + +static void max11615_reset(Object *obj, ResetType type) +{ + MAX11615State *s =3D MAX11615(obj); + + max11615_reset_enter(s); +} + +static void max11615_realize(DeviceState *dev, Error **errp) +{ + MAX11615State *s =3D MAX11615(dev); + + trace_max11615_realize(s->i2c.address); +} + +static void max11615_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc =3D DEVICE_CLASS(klass); + I2CSlaveClass *k =3D I2C_SLAVE_CLASS(klass); + ResettableClass *rc =3D RESETTABLE_CLASS(klass); + + dc->realize =3D max11615_realize; + dc->desc =3D "Maxim MAX11615 12-bit ADC"; + dc->vmsd =3D &vmstate_max11615; + rc->phases.hold =3D max11615_reset; + k->event =3D max11615_event; + k->recv =3D max11615_recv; + k->send =3D max11615_send; +} + +static const TypeInfo max11615_info =3D { + .name =3D TYPE_MAX11615, + .parent =3D TYPE_I2C_SLAVE, + .instance_size =3D sizeof(MAX11615State), + .class_size =3D sizeof(MAX11615Class), + .class_init =3D max11615_class_init, +}; + +static void max11615_register_types(void) +{ + type_register_static(&max11615_info); +} + +type_init(max11615_register_types) diff --git a/hw/sensor/meson.build b/hw/sensor/meson.build index 4987c3b253..a1e26604fa 100644 --- a/hw/sensor/meson.build +++ b/hw/sensor/meson.build @@ -9,3 +9,4 @@ system_ss.add(when: 'CONFIG_LSM303DLHC_MAG', if_true: files= ('lsm303dlhc_mag.c')) system_ss.add(when: 'CONFIG_ISL_PMBUS_VR', if_true: files('isl_pmbus_vr.c'= )) system_ss.add(when: 'CONFIG_MAX31785', if_true: files('max31785.c')) system_ss.add(when: 'CONFIG_MAX31790', if_true: files('max31790.c')) +system_ss.add(when: 'CONFIG_MAX11615', if_true: files('max11615.c')) diff --git a/hw/sensor/trace-events b/hw/sensor/trace-events index 35c30b985c..1512819354 100644 --- a/hw/sensor/trace-events +++ b/hw/sensor/trace-events @@ -17,3 +17,12 @@ max31790_fan_dynamics_read(uint8_t i2c_addr, size_t chan= nel, uint8_t value) "i2c max31790_fan_dynamics_write(uint8_t i2c_addr, size_t channel, uint8_t valu= e) "i2c_addr: 0x%02x, channel: %zu, value: 0x%02x" max31790_fan_config_read(uint8_t i2c_addr, size_t channel, uint8_t value) = "i2c_addr: 0x%02x, channel: %zu, value: 0x%02x" max31790_fan_config_write(uint8_t i2c_addr, size_t channel, uint8_t value)= "i2c_addr: 0x%02x, channel: %zu, value: 0x%02x" + +# max11615.c +max11615_write_setup(uint8_t i2c_addr, uint8_t send) "i2c_addr: 0x%02x, da= ta: 0x%02x" +max11615_write_config(uint8_t i2c_addr, uint8_t send) "i2c_addr: 0x%02x, d= ata: 0x%02x" +max11615_recv(uint8_t i2c_addr, uint8_t reg_addr) "i2c_addr: 0x%02x, reg_a= ddr: 0x%02x" +max11615_recv_return(uint8_t i2c_addr, uint8_t data) "i2c_addr: 0x%02x, re= turns: 0x%02x" +max11615_event(uint8_t i2c_addr, uint8_t event) "i2c_addr: 0x%02x, event: = 0x%02x" +max11615_realize(uint8_t i2c_addr) "i2c_addr: 0x%02x" +max11615_reset_enter(uint8_t i2c_addr) "i2c_addr: 0x%02x" diff --git a/include/hw/sensor/max11615.h b/include/hw/sensor/max11615.h new file mode 100644 index 0000000000..647ef62ef1 --- /dev/null +++ b/include/hw/sensor/max11615.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#ifndef QEMU_MAX11615_H +#define QEMU_MAX11615_H + +#include "hw/i2c/i2c.h" + +#define TYPE_MAX11615 "max11615" + +/* + * Create and realize a MAX11615 ADC with constant caller-supplied readings + * @bus: I2C bus to put it on + * @address: I2C address + * @init_values: array of readings for each ADC channel + * @init_values_size: Size of @init_values, can be less than the number of= channels + */ +I2CSlave *max11615_init_with_values(I2CBus *bus, uint8_t address, + const uint16_t *init_values, uint32_t = init_values_size); + +#endif diff --git a/tests/functional/arm/test_aspeed_fby4.py b/tests/functional/ar= m/test_aspeed_fby4.py index f344413fe4..98223e3ab1 100755 --- a/tests/functional/arm/test_aspeed_fby4.py +++ b/tests/functional/arm/test_aspeed_fby4.py @@ -81,6 +81,15 @@ def do_test_arm_aspeed_openbmc_no_network(self, machine,= image, uboot, exec_command_and_wait_for_pattern(self, "cat /sys/class/hwmon/hwmon2/fan1_input", "10347"); =20 + # MAX11615 test + exec_command_and_wait_for_pattern(self, + "cat /sys/bus/i2c/devices/30-0033/iio:device*/name", "max11615= "); + # read the value configured in the machine emulation + exec_command_and_wait_for_pattern(self, + "cat /sys/bus/i2c/devices/30-0033/iio:device*/in_voltage0_raw"= , "1922"); + exec_command_and_wait_for_pattern(self, + "cat /sys/bus/i2c/devices/30-0033/iio:device*/in_voltage_scale= ", "0.500000000"); + def test_arm_ast2600_yosemitev4_openbmc(self): image_path =3D self.uncompress(self.ASSET_YOSEMITE_V4_FLASH) =20 --=20 2.54.0