From patchwork Sat Apr 3 22:28:09 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Patrick Leis X-Patchwork-Id: 12182025 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-26.3 required=3.0 tests=BAYES_00,DKIMWL_WL_MED, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS, USER_AGENT_GIT,USER_IN_DEF_DKIM_WL autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 78AB2C433B4 for ; Sat, 3 Apr 2021 22:29:36 +0000 (UTC) Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id DE55D61359 for ; Sat, 3 Apr 2021 22:29:35 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org DE55D61359 Authentication-Results: mail.kernel.org; dmarc=fail (p=reject dis=none) header.from=google.com Authentication-Results: mail.kernel.org; spf=pass smtp.mailfrom=qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Received: from localhost ([::1]:45464 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1lSolq-0005RV-RB for qemu-devel@archiver.kernel.org; Sat, 03 Apr 2021 18:29:34 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:35676) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from <3C-xoYAcKCpAFy7DEBy08805y.w86Ay6E-xyFy578707E.8B0@flex--venture.bounces.google.com>) id 1lSokx-0004WV-1J for qemu-devel@nongnu.org; Sat, 03 Apr 2021 18:28:42 -0400 Received: from mail-yb1-xb49.google.com ([2607:f8b0:4864:20::b49]:48853) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from <3C-xoYAcKCpAFy7DEBy08805y.w86Ay6E-xyFy578707E.8B0@flex--venture.bounces.google.com>) id 1lSokn-0002pD-Aa for qemu-devel@nongnu.org; Sat, 03 Apr 2021 18:28:34 -0400 Received: by mail-yb1-xb49.google.com with SMTP id d1so12995762ybj.15 for ; Sat, 03 Apr 2021 15:28:28 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=T5YtRkRKUUIOvoZmqL262NcISbGPOODubGnB2CtWpAQ=; b=EI1a+iI7jyULXMrxo+kTtXgWo9gNO8ityODs5UgbjMSa5NClRfyU18fWCeQ3Y4kDiw QN0eaxm2gjt/ZSR388Zk/UdDSnZRm9iSEO1iKinhI61DbnP6LSjaMgR7gDpTfzy7TfTG MsmOUkO1EzTE6C31S8h4D0ACzRmvphnTZ7NJNByQV48T5eW73rRjV2Boz9CY0GgyywxV fkNFhtPDmegDkO1+c3Kj4EJZQk+djo3lxATmVoJ9By8IkruUHbbEdMA80rfCKUOpC+8T ZHITpdMBkDVqTGoInKfrIHwPC1Jz3gIfiV/p6nkxXeIzozcI3XDC3gb/fM+ip2XUIml2 yBzQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=T5YtRkRKUUIOvoZmqL262NcISbGPOODubGnB2CtWpAQ=; b=Ie7mGEHYFppndQkQHi4YfBp9BedMsRdS8Y7cNOYbKaUL+qkDp+RkRTVLLK3kOMFcuQ GZpDGXjIzzPnqFh53KTerqqJaM0mIN/xefJLeDj2atawyLdrhVqJQpPJJm0U7wtWpwdL b6dmzeazmPea1VOFhWsIxBtoHKb4tE2apWbNZS4q8H1vDbUwXvnLKnI2LSQ1Gb2YLqPM OEt+j33CYFXLLS9ocm+d5uJHaz9JCXc2S3i86EIoGQrOIXylcMdv0FNNQl4qW/irIv2u nb2Uf9tKDnmgfAz57g946+7ZoeBcPfc00Gq628/VoM0JNvo/GRxX5WFhC7GyQEFFeBDB PLIA== X-Gm-Message-State: AOAM533Klzej0Dc1ZzxINma6ZkyY/lo+xmFYBftjPT20LZ4DzdtJQEQZ mWKGBtPmkBxWHCbTdFZfuAkuR1dnY6yD X-Google-Smtp-Source: ABdhPJyHyXOTrSr939Tw9t5NhVRSIeYTikJIU7ILxZKHOG8pG4+aNYMZj4FUTQGiqNSwtJUOK5xVgEh2EoA+ X-Received: from venture.svl.corp.google.com ([2620:15c:2a3:200:bcbb:2e0c:25df:d735]) (user=venture job=sendgmr) by 2002:a25:db42:: with SMTP id g63mr27328821ybf.404.1617488907285; Sat, 03 Apr 2021 15:28:27 -0700 (PDT) Date: Sat, 3 Apr 2021 15:28:09 -0700 In-Reply-To: <20210403222810.3481372-1-venture@google.com> Message-Id: <20210403222810.3481372-2-venture@google.com> Mime-Version: 1.0 References: <20210403222810.3481372-1-venture@google.com> X-Mailer: git-send-email 2.31.0.208.g409f899ff0-goog Subject: [PATCH 1/2] hw/i2c/core: add reachable state boolean From: Patrick Venture To: cminyard@mvista.com, wuhaotsh@google.com, hskinnemoen@google.com Cc: qemu-arm@nongnu.org, qemu-devel@nongnu.org, Patrick Venture Received-SPF: pass client-ip=2607:f8b0:4864:20::b49; envelope-from=3C-xoYAcKCpAFy7DEBy08805y.w86Ay6E-xyFy578707E.8B0@flex--venture.bounces.google.com; helo=mail-yb1-xb49.google.com X-Spam_score_int: -95 X-Spam_score: -9.6 X-Spam_bar: --------- X-Spam_report: (-9.6 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_MED=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, USER_IN_DEF_DKIM_WL=-7.5 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Sender: "Qemu-devel" An i2c device can be reachable or not, controlled by some external factor. This field is leveraged by an i2c mux which presents the devices on the parent bus when the associated channel is enabled and otherwise not. Signed-off-by: Patrick Venture Reviewed-by: Havard Skinnemoen Reviewed-by: Hao Wu --- hw/i2c/core.c | 6 ++++++ include/hw/i2c/i2c.h | 3 +++ 2 files changed, 9 insertions(+) diff --git a/hw/i2c/core.c b/hw/i2c/core.c index 21ec52ac5a..fa7db4549d 100644 --- a/hw/i2c/core.c +++ b/hw/i2c/core.c @@ -18,6 +18,7 @@ #define I2C_BROADCAST 0x00 static Property i2c_props[] = { + DEFINE_PROP_BOOL("reachable", struct I2CSlave, reachable, true), DEFINE_PROP_UINT8("address", struct I2CSlave, address, 0), DEFINE_PROP_END_OF_LIST(), }; @@ -118,6 +119,9 @@ int i2c_start_transfer(I2CBus *bus, uint8_t address, int recv) QTAILQ_FOREACH(kid, &bus->qbus.children, sibling) { DeviceState *qdev = kid->child; I2CSlave *candidate = I2C_SLAVE(qdev); + if (!candidate->reachable) { + continue; + } if ((candidate->address == address) || (bus->broadcast)) { node = g_malloc(sizeof(struct I2CNode)); node->elt = candidate; @@ -262,6 +266,7 @@ const VMStateDescription vmstate_i2c_slave = { .minimum_version_id = 1, .post_load = i2c_slave_post_load, .fields = (VMStateField[]) { + VMSTATE_BOOL(reachable, I2CSlave), VMSTATE_UINT8(address, I2CSlave), VMSTATE_END_OF_LIST() } @@ -272,6 +277,7 @@ I2CSlave *i2c_slave_new(const char *name, uint8_t addr) DeviceState *dev; dev = qdev_new(name); + qdev_prop_set_bit(dev, "reachable", true); qdev_prop_set_uint8(dev, "address", addr); return I2C_SLAVE(dev); } diff --git a/include/hw/i2c/i2c.h b/include/hw/i2c/i2c.h index 277dd9f2d6..e5ca15e486 100644 --- a/include/hw/i2c/i2c.h +++ b/include/hw/i2c/i2c.h @@ -44,6 +44,9 @@ struct I2CSlaveClass { struct I2CSlave { DeviceState qdev; + /* Whether the i2c child device is reachable from this bus. */ + bool reachable; + /* Remaining fields for internal use by the I2C code. */ uint8_t address; }; From patchwork Sat Apr 3 22:28:10 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Patrick Leis X-Patchwork-Id: 12182027 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-26.2 required=3.0 tests=BAYES_00,DKIMWL_WL_MED, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS, URIBL_BLOCKED,USER_AGENT_GIT,USER_IN_DEF_DKIM_WL autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id AA06FC433B4 for ; Sat, 3 Apr 2021 22:30:34 +0000 (UTC) Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 21A5561351 for ; Sat, 3 Apr 2021 22:30:34 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 21A5561351 Authentication-Results: mail.kernel.org; dmarc=fail (p=reject dis=none) header.from=google.com Authentication-Results: mail.kernel.org; spf=pass smtp.mailfrom=qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Received: from localhost ([::1]:48982 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1lSomn-0006rl-Ad for qemu-devel@archiver.kernel.org; Sat, 03 Apr 2021 18:30:33 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:35698) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from <3DuxoYAcKCpMI1AGHE13BB381.zB9D19H-01I18ABA3AH.BE3@flex--venture.bounces.google.com>) id 1lSol1-0004XM-JH for qemu-devel@nongnu.org; Sat, 03 Apr 2021 18:28:44 -0400 Received: from mail-yb1-xb4a.google.com ([2607:f8b0:4864:20::b4a]:42670) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from <3DuxoYAcKCpMI1AGHE13BB381.zB9D19H-01I18ABA3AH.BE3@flex--venture.bounces.google.com>) id 1lSokr-0002qb-Ms for qemu-devel@nongnu.org; Sat, 03 Apr 2021 18:28:41 -0400 Received: by mail-yb1-xb4a.google.com with SMTP id v6so13020794ybk.9 for ; Sat, 03 Apr 2021 15:28:31 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=9Rr9JYnLZhIsJpXVKMtSmriKFQyc6Y0e2KboV1hbcLc=; b=YiX0mdKCIDkL6MkHKCii3Ynh4PUSia4/bEzshjGpXjnLK3nDtV4NLhq1nNU1v0oVLT vPmQwwAGjxNFtBi6Mt3i1TvMTC5U2N2gCjyE61CTavV8L5oNmvrQ/sV4m4rijcGvG3+6 k5OoJpXFBN1KavC0eMxRA6Pel4CaUAdvHz+MZrH9wEdr8fDF6ZEoIad40E5Tx6cxTuZd h0WKcO61Z5zh+TKqfGghA2lmz6BlJmqkQ2kyqlBfmQlmlbHJmU5EYs0oElPhcTRw9+yM k5xCm93A+ei8trYve2kOwQ2U4EV88MFPizuW1DQ9b+Sbd84bgojrRnIsvHTVgZv3jMaO 9bfg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=9Rr9JYnLZhIsJpXVKMtSmriKFQyc6Y0e2KboV1hbcLc=; b=UossRLtj1huQrHTNyYMY98wqkTWcW7G7hreaFs6TFp1Wqq/L2PEf7JAbvqbYhbQyhV X0vwBpHKH32/Cy1wVI3Xxafq0EBtgueJijGeSrWJ2eBbmjPSi3YpCVUD5l5Je4hrTnkJ o32P1Rdi8QlAW4aQdp8WW0c6BT8YUXg7DP440YZZoxDIVChZBKYFyqWjZ6r2vzIVmnYl o/v/zvndpR0qnBCh0WTeBkm8knSBbYNH7EsXJAFjwNihNkBwDHzJP/qrow1c97+jFAqH HWVhieD4U5oBhrHrC6sYh2k01nddu7iyW1IICM8dil9JUa1Aw19FAafFGnJTVkL5J0Om bikg== X-Gm-Message-State: AOAM532a3bnPdHM6fE3JjwER/ymQ140UL6S5hUJTKMX2VXJU3lMxhd4l P7FbzYHHqKwy0CFCv2VEDGpTUHI34E3G X-Google-Smtp-Source: ABdhPJzo23Cu2rf3f0QdTOhAQiy9R9brwPOrZ3bRyBHzgeeyn3ee21p3wUdwKsJOVT38xaRsmryIwFGgmAFg X-Received: from venture.svl.corp.google.com ([2620:15c:2a3:200:bcbb:2e0c:25df:d735]) (user=venture job=sendgmr) by 2002:a25:424c:: with SMTP id p73mr27279461yba.192.1617488910866; Sat, 03 Apr 2021 15:28:30 -0700 (PDT) Date: Sat, 3 Apr 2021 15:28:10 -0700 In-Reply-To: <20210403222810.3481372-1-venture@google.com> Message-Id: <20210403222810.3481372-3-venture@google.com> Mime-Version: 1.0 References: <20210403222810.3481372-1-venture@google.com> X-Mailer: git-send-email 2.31.0.208.g409f899ff0-goog Subject: [PATCH 2/2] hw/i2c: add pca954x i2c-mux switch From: Patrick Venture To: cminyard@mvista.com, wuhaotsh@google.com, hskinnemoen@google.com Cc: qemu-arm@nongnu.org, qemu-devel@nongnu.org, Patrick Venture Received-SPF: pass client-ip=2607:f8b0:4864:20::b4a; envelope-from=3DuxoYAcKCpMI1AGHE13BB381.zB9D19H-01I18ABA3AH.BE3@flex--venture.bounces.google.com; helo=mail-yb1-xb4a.google.com X-Spam_score_int: -95 X-Spam_score: -9.6 X-Spam_bar: --------- X-Spam_report: (-9.6 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_MED=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, USER_IN_DEF_DKIM_WL=-7.5 autolearn=unavailable autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Sender: "Qemu-devel" The pca954x is an i2c mux, and this adds support for two variants of this device: the pca9546 and pca9548. This device is very common on BMCs to route a different channel to each PCIe i2c bus downstream from the BMC. Signed-off-by: Patrick Venture Reviewed-by: Havard Skinnemoen Reviewed-by: Hao Wu --- MAINTAINERS | 6 + hw/i2c/Kconfig | 4 + hw/i2c/i2c_mux_pca954x.c | 182 +++++++++++++++++++++++++++++++ hw/i2c/meson.build | 1 + hw/i2c/trace-events | 5 + include/hw/i2c/i2c_mux_pca954x.h | 60 ++++++++++ 6 files changed, 258 insertions(+) create mode 100644 hw/i2c/i2c_mux_pca954x.c create mode 100644 include/hw/i2c/i2c_mux_pca954x.h diff --git a/MAINTAINERS b/MAINTAINERS index 69003cdc3c..6cec2a9320 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2040,6 +2040,12 @@ S: Maintained F: hw/net/tulip.c F: hw/net/tulip.h +pca954x +M: Patrick Venture +S: Maintained +F: hw/i2c/i2c_mux_pca954x.c +F: include/hw/i2c/i2c_mux_pca954x.h + Generic Loader M: Alistair Francis S: Maintained diff --git a/hw/i2c/Kconfig b/hw/i2c/Kconfig index 09642a6dcb..8d120a25d5 100644 --- a/hw/i2c/Kconfig +++ b/hw/i2c/Kconfig @@ -28,3 +28,7 @@ config IMX_I2C config MPC_I2C bool select I2C + +config PCA954X + bool + select I2C diff --git a/hw/i2c/i2c_mux_pca954x.c b/hw/i2c/i2c_mux_pca954x.c new file mode 100644 index 0000000000..8017913637 --- /dev/null +++ b/hw/i2c/i2c_mux_pca954x.c @@ -0,0 +1,182 @@ +/* + * I2C multiplexer for PCA954x series of I2C multiplexer/switch chips. + * + * Copyright 2021 Google LLC + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "hw/i2c/i2c_mux_pca954x.h" +#include "hw/i2c/smbus_slave.h" +#include "hw/qdev-core.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "qemu/queue.h" +#include "trace.h" + +int pca954x_add_child(I2CSlave *mux, uint8_t channel, I2CSlave *child) +{ + g_autofree char *name = NULL; + /* + * Ok, so we need to try to add the i2c_dev to channel for mux. + * A channel can have multiple devices, we need a list for each channel. + */ + Pca954xClass *pc = PCA954X_GET_CLASS(mux); + Pca954xState *pca954x = PCA954X(mux); + PcaMuxChild *controlled_device; + + /* Is the channel legal? */ + if (channel >= pc->nchans) { + return -1; + } + + controlled_device = g_new0(PcaMuxChild, 1); + controlled_device->channel = channel; + controlled_device->child = child; + object_ref(OBJECT(controlled_device->child)); + + /* Hide the device. */ + child->reachable = 0; + + QSLIST_INSERT_HEAD(&pca954x->children, controlled_device, sibling); + + name = g_strdup_printf("i2c@%u-child[%u]", channel, + pca954x->count[channel]); + object_property_add_link(OBJECT(mux), name, + object_get_typename(OBJECT(child)), + (Object **)&controlled_device->child, + NULL, /* read-only property */ + 0); + pca954x->count[channel]++; + + return 0; +} + +static void pca954x_enable_channel(Pca954xState *s, uint8_t enable_mask) +{ + PcaMuxChild *kid; + I2CSlave *child; + + /* + * For each child, check if their bit is set in data and if yes, enable + * them, otherwise disable, hide them. + */ + QSLIST_FOREACH(kid, &s->children, sibling) { + child = I2C_SLAVE(kid->child); + if (enable_mask & (1 << kid->channel)) { + child->reachable = true; + } else { + child->reachable = false; + } + } +} + +static void pca954x_write(Pca954xState *s, uint8_t data) +{ + s->control = data; + pca954x_enable_channel(s, data); + + trace_pca954x_write_bytes(data); +} + +static int pca954x_write_data(SMBusDevice *d, uint8_t *buf, uint8_t len) +{ + Pca954xState *s = PCA954X(d); + + if (len == 0) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: writing empty data\n", __func__); + return -1; + } + + /* + * len should be 1, because they write one byte to enable/disable channels. + */ + if (len > 1) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: extra data after channel selection mask\n", + __func__); + return -1; + } + + pca954x_write(s, buf[0]); + return 0; +} + +static uint8_t pca954x_read_byte(SMBusDevice *d) +{ + Pca954xState *s = PCA954X(d); + uint8_t data = s->control; + trace_pca954x_read_data(data); + return data; +} + +static void pca9546_class_init(ObjectClass *oc, void *data) +{ + Pca954xClass *s = PCA954X_CLASS(oc); + s->nchans = PCA9546_CHANNEL_COUNT; +} + +static void pca9548_class_init(ObjectClass *oc, void *data) +{ + Pca954xClass *s = PCA954X_CLASS(oc); + s->nchans = PCA9548_CHANNEL_COUNT; +} + +static void pca954x_enter_reset(Object *obj, ResetType type) +{ + Pca954xState *s = PCA954X(obj); + /* Reset will disable all channels. */ + pca954x_write(s, 0); +} + +static void pca954x_init(Object *obj) +{ + Pca954xState *s = PCA954X(obj); + memset(s->count, 0x00, sizeof(s->count)); +} + +static void pca954x_class_init(ObjectClass *klass, void *data) +{ + ResettableClass *rc = RESETTABLE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + SMBusDeviceClass *k = SMBUS_DEVICE_CLASS(klass); + + dc->desc = "Pca954x i2c-mux"; + k->write_data = pca954x_write_data; + k->receive_byte = pca954x_read_byte; + rc->phases.enter = pca954x_enter_reset; +} + +static const TypeInfo pca954x_info[] = { + { + .name = TYPE_PCA954X, + .parent = TYPE_SMBUS_DEVICE, + .instance_size = sizeof(Pca954xState), + .instance_init = pca954x_init, + .class_size = sizeof(Pca954xClass), + .class_init = pca954x_class_init, + .abstract = true, + }, + { + .name = TYPE_PCA9546, + .parent = TYPE_PCA954X, + .class_init = pca9546_class_init, + }, + { + .name = TYPE_PCA9548, + .parent = TYPE_PCA954X, + .class_init = pca9548_class_init, + }, +}; + +DEFINE_TYPES(pca954x_info) diff --git a/hw/i2c/meson.build b/hw/i2c/meson.build index cdcd694a7f..dd3aef02b2 100644 --- a/hw/i2c/meson.build +++ b/hw/i2c/meson.build @@ -14,4 +14,5 @@ i2c_ss.add(when: 'CONFIG_SMBUS_EEPROM', if_true: files('smbus_eeprom.c')) i2c_ss.add(when: 'CONFIG_VERSATILE_I2C', if_true: files('versatile_i2c.c')) i2c_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_i2c.c')) i2c_ss.add(when: 'CONFIG_PPC4XX', if_true: files('ppc4xx_i2c.c')) +i2c_ss.add(when: 'CONFIG_PCA954X', if_true: files('i2c_mux_pca954x.c')) softmmu_ss.add_all(when: 'CONFIG_I2C', if_true: i2c_ss) diff --git a/hw/i2c/trace-events b/hw/i2c/trace-events index 82fe6f965f..82f19e6a2d 100644 --- a/hw/i2c/trace-events +++ b/hw/i2c/trace-events @@ -26,3 +26,8 @@ npcm7xx_smbus_recv_byte(const char *id, uint8_t value) "%s recv byte: 0x%02x" npcm7xx_smbus_stop(const char *id) "%s stopping" npcm7xx_smbus_nack(const char *id) "%s nacking" npcm7xx_smbus_recv_fifo(const char *id, uint8_t received, uint8_t expected) "%s recv fifo: received %u, expected %u" + +# i2c-mux-pca954x.c + +pca954x_write_bytes(uint8_t value) "PCA954X write data: 0x%02x" +pca954x_read_data(uint8_t value) "PCA954X read data: 0x%02x" diff --git a/include/hw/i2c/i2c_mux_pca954x.h b/include/hw/i2c/i2c_mux_pca954x.h new file mode 100644 index 0000000000..4ea240af26 --- /dev/null +++ b/include/hw/i2c/i2c_mux_pca954x.h @@ -0,0 +1,60 @@ +#ifndef QEMU_I2C_MUX_PCA954X +#define QEMU_I2C_MUX_PCA954X + +#include "hw/qdev-core.h" +#include "hw/i2c/i2c.h" +#include "hw/i2c/smbus_slave.h" + +#define PCA9548_CHANNEL_COUNT 8 +#define PCA9546_CHANNEL_COUNT 4 + +/* The i2c mux shares ownership of a bus child. */ +typedef struct PcaMuxChild { + I2CSlave *child; + + /* The channel on which this child lives. */ + uint8_t channel; + + QSLIST_ENTRY(PcaMuxChild) sibling; +} PcaMuxChild; + +typedef struct Pca954xState { + SMBusDevice parent; + + uint8_t control; + + /* The children this mux co-owns with its parent bus. */ + QSLIST_HEAD(, PcaMuxChild) children; + + /* The number of children per channel. */ + unsigned int count[PCA9548_CHANNEL_COUNT]; +} Pca954xState; + +typedef struct Pca954xClass { + SMBusDeviceClass parent; + + /* The number of channels this mux has. */ + uint8_t nchans; +} Pca954xClass; + +#define TYPE_PCA9546 "pca9546" +#define TYPE_PCA9548 "pca9548" + +#define TYPE_PCA954X "pca954x" + +#define PCA954X(obj) OBJECT_CHECK(Pca954xState, (obj), TYPE_PCA954X) +#define PCA954X_CLASS(klass) \ + OBJECT_CLASS_CHECK(Pca954xClass, (klass), TYPE_PCA954X) +#define PCA954X_GET_CLASS(obj) \ + OBJECT_GET_CLASS(Pca954xClass, (obj), TYPE_PCA954X) + +/** + * pca954x_add_child - Adds a child i2c device to the mux device on the + * specified channel. + * @mux - The i2c mux to control this child. + * @channel - The channel of the i2c mux that gates this child. + * @child - The i2c child device to add to the mux. + */ +int pca954x_add_child(I2CSlave *mux, uint8_t channel, I2CSlave *child); + +#endif