From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-wm1-f54.google.com (mail-wm1-f54.google.com [209.85.128.54]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id F0BCA397B1C for ; Sun, 2 Aug 2026 10:01:25 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.54 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785664888; cv=none; b=NGeDQGRnavpJjXzNzAgA79HzcRyBEY/k2qulc6uwby9KCkpqjMP7fZPBMkjf7L6nEsmjULKoGLl3GHwTKCcBizG22VpfrAs4rBMNoqZB/oCk4/jyV3exXS0R2th0RmneqQHKZr8G50Nc2H01MieI66Niu+0pn57dfo59Q72uXio= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785664888; c=relaxed/simple; bh=05FD9Q8MiGxbmhMqb3xelmGLNDiAwBg44gqAs0Q5qYc=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=KhHYSgbT41cPkFEJyLD8lJ3BFyZG7c00V3nkvyM9deorUaMFdvU4kHxj8d0Q4p5kPNz+/xrL6PLmBleukyJLdUM3wHXutOjz9ar1bfCkfTlRwqaF7PIZ+VRJ5TKGsPQtj82ep+TLO5VVz1B4EoILUZThPUPc9NhZTMFN5LjKaes= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=d0EwivEp; arc=none smtp.client-ip=209.85.128.54 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="d0EwivEp" Received: by mail-wm1-f54.google.com with SMTP id 5b1f17b1804b1-4953e04ef16so11691525e9.2 for ; Sun, 02 Aug 2026 03:01:25 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1785664884; x=1786269684; darn=vger.kernel.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:content-type; bh=mVaU5aBNCpHteSlHa9KIXjavIsGZ1Ok+bRtNiOCJLQ4=; b=d0EwivEpeaebP9cl4J3g1DsF04DVuzA+Zg8IkdgH5tj4AKYncYE7dJrfr8+XvUNerI 8kgHeW/zc3jD+ftGHKKNeDFe5yq93ttUTioVQCFMuB4Bgon2l7BEqgCzpTX7ZoGKtOX4 AHdV9XItjxSTPVxbxwXz/NQo5SU3BchB5tYCQ5KSF7EjkDZQMH21dDRjr2l3MuZ8VuAE klDflXXflvH1tFl2jmMgG88G9GJgrI7BoT2e89qdsKD2cB8FbKi6M3KUxYebe8RIhZgg ZIa5zXZuyJSg3Z53WZwpPKrVuox/AKSqqbYavaxWB/U9lTYAyb4oxbrQhCNJmAdHXAWJ 7Plw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1785664884; x=1786269684; 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:content-type; bh=mVaU5aBNCpHteSlHa9KIXjavIsGZ1Ok+bRtNiOCJLQ4=; b=bO+xYyWZPG5KYxAJmKL7HKvHQ3i58EMlu1/V0vyu4OUltwjlJhKomuk1KX8/t0t3WQ XB9QMqtAM/kLW1ta/M8J6LUB8r271918bHB1drny8Xauw0h8X4vBKbM86EjIBz83TQrT oSwA8508emOABIW/P9ekc3blX1J+a7iymAA4oaCQVlXBwFZasQOP8iEwDAXdzyJq9g2N XLd+rYgjPuON2/FPa24ET6pn7PG1MJF8l2EsHz5EgngPv7I1gN4GkgIrlyHecrssGPqx uCpHdmdxvi/TUpq2lWbMzCJUUIy24jzeVA8t/ulIylqXMxDUH4eucM+bnBLPeMdO95B2 22Fg== X-Forwarded-Encrypted: i=1; AHgh+RoyeWg2A3RcwOFkwLnXwCr11Guddap1ckfNy/ItGOg1y0pebnnrmwr6BR07PLnvPiWZabZfHhFaKp2LEQI=@vger.kernel.org X-Gm-Message-State: AOJu0YzPSLFNVx/g+EA6GpyALLrQ9vhV/uz8cVktOuCoIV1S3HJWSIxz Wu7lzoVteJHsw1yFIgnSFZiDlwA29PqM/aQqRr8W6jOpcSKG5MfItbEN X-Gm-Gg: AR+sD11qAKnjZu2autnKrLnccffKnarVbQlHxwl9GLa+2M5figUQdJPrXSbaoGy/ZuE e0zqUgcsJBEWy+R7u2YE3/yVXC3VZ0tZ8w5kptgWFanJVZylQlvhe3FQa3mUqKgsK9jvYI2xhzq nXgerd75FoQsDRdm5IjiHUYJs0QsWZBQUfwwxWSRMWcc9m4L5UfBKvB70DYm5NScOEpX3CYwLSL tKiT6sUSiyypEz8tDp/Fk9S0p5bexTehqVeT8e+n1umIQm5Spa6h+TQ0acqgc9s6MF+gyKOO0x1 RcVlTnue4QSz0dLQeciqnmdI0QhTRejBypz0IHZZ3ofsvWBLR80zpHQN/bvqk39kSU9Q44oM8ee Xoy1gAgcUn1+Xnfu+cUdJOQxc5T4EkeFRA6LsD+NnnfQspPdrd7oJOsUDFmptrj8b7U+c/5C+NU coUxnO41OGWuGunhn+JPDhn7syuvthVuE8I0vYebngFLMwlPCROfYRfMP51h44zLs= X-Received: by 2002:a05:600c:4ec9:b0:495:62bc:a022 with SMTP id 5b1f17b1804b1-4980c67474amr102207435e9.13.1785664883983; Sun, 02 Aug 2026 03:01:23 -0700 (PDT) Received: from builder ([2001:9e8:f117:b116:be24:11ff:fe30:5d85]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-49807b5b4a2sm140991375e9.1.2026.08.02.03.01.23 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 02 Aug 2026 03:01:23 -0700 (PDT) From: Jonas Jelonek To: Oleksij Rempel , Kory Maincent , Andrew Lunn , "David S . Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Rob Herring , Krzysztof Kozlowski , Conor Dooley Cc: netdev@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, Daniel Golle , =?UTF-8?q?Bj=C3=B8rn=20Mork?= , Sander Vanheule , Jonas Jelonek Subject: [PATCH net-next v11 3/4] net: pse-pd: realtek-pse-mcu: add I2C transport Date: Sun, 2 Aug 2026 10:01:10 +0000 Message-ID: <20260802100114.720594-4-jelonek.jonas@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260802100114.720594-1-jelonek.jonas@gmail.com> References: <20260802100114.720594-1-jelonek.jonas@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Add the I2C/SMBus transport for the Realtek PSE MCU core. It registers the MCU on an I2C bus and provides the send/recv callbacks the core uses to exchange the 12-byte frames. The MCU firmware expects one of two framings on the I2C bus, and which one is part of the compatible: '-smbus' (reads carry a leading command byte and a repeated start) or raw '-i2c' (bare block writes and reads). The match data flags the raw-I2C case; SMBus is the default because that's what the majority of devices uses. Because i2c_master_send()/i2c_master_recv() may DMA, the raw-I2C path bounces each frame through a heap buffer rather than the core's stack buffers; the SMBus path is unaffected. Signed-off-by: Jonas Jelonek Acked-by: Oleksij Rempel Reviewed-by: Kory Maincent --- drivers/net/pse-pd/Kconfig | 11 ++ drivers/net/pse-pd/Makefile | 1 + drivers/net/pse-pd/realtek-pse-mcu-i2c.c | 170 +++++++++++++++++++++++ 3 files changed, 182 insertions(+) create mode 100644 drivers/net/pse-pd/realtek-pse-mcu-i2c.c diff --git a/drivers/net/pse-pd/Kconfig b/drivers/net/pse-pd/Kconfig index 3b0c245a2bc7..6d14c8832e8b 100644 --- a/drivers/net/pse-pd/Kconfig +++ b/drivers/net/pse-pd/Kconfig @@ -19,6 +19,17 @@ config PSE_REALTEK_MCU Shared core for the Realtek PSE MCU driver. This is selected automatically by the transport options below. +config PSE_REALTEK_MCU_I2C + tristate "Realtek PSE MCU driver (I2C transport)" + depends on I2C + select PSE_REALTEK_MCU + help + Driver for the microcontroller (MCU) that fronts the PSE + hardware on various Realtek-based managed switches, attached + via I2C/SMBus. The MCU exposes a message-based protocol; the actual + PSE silicon is not accessed directly. To compile this driver as a + module, choose M here: the module will be called realtek-pse-mcu-i2c. + config PSE_REGULATOR tristate "Regulator based PSE controller" help diff --git a/drivers/net/pse-pd/Makefile b/drivers/net/pse-pd/Makefile index bf35e2a5b110..ef869bba5ed9 100644 --- a/drivers/net/pse-pd/Makefile +++ b/drivers/net/pse-pd/Makefile @@ -4,6 +4,7 @@ obj-$(CONFIG_PSE_CONTROLLER) += pse_core.o obj-$(CONFIG_PSE_REALTEK_MCU) += realtek-pse-mcu-core.o +obj-$(CONFIG_PSE_REALTEK_MCU_I2C) += realtek-pse-mcu-i2c.o obj-$(CONFIG_PSE_REGULATOR) += pse_regulator.o obj-$(CONFIG_PSE_PD692X0) += pd692x0.o obj-$(CONFIG_PSE_SI3474) += si3474.o diff --git a/drivers/net/pse-pd/realtek-pse-mcu-i2c.c b/drivers/net/pse-pd/realtek-pse-mcu-i2c.c new file mode 100644 index 000000000000..e10f8c633b68 --- /dev/null +++ b/drivers/net/pse-pd/realtek-pse-mcu-i2c.c @@ -0,0 +1,170 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include +#include +#include +#include +#include + +#include "realtek-pse-mcu.h" + +/* + * The core has already waited RTPSE_MCU_RESPONSE_MS before calling us, so + * the response is normally ready on the very first read. For commands the + * MCU produces more slowly, keep polling at the typical response cadence + * up to the worst-case ceiling. + */ +#define RTPSE_MCU_I2C_RETRY_MS RTPSE_MCU_RESPONSE_MS +#define RTPSE_MCU_I2C_MAX_TRIES (RTPSE_MCU_RESPONSE_MAX_MS / RTPSE_MCU_I2C_RETRY_MS) + +static int rtpse_mcu_i2c_smbus_send(struct rtpse_mcu_ctrl *pse, const struct rtpse_mcu_msg *req) +{ + struct i2c_client *client = to_i2c_client(pse->dev); + + /* Send opcode as SMBus command byte; remaining 11 bytes as block data */ + return i2c_smbus_write_i2c_block_data(client, req->opcode, RTPSE_MCU_MSG_SIZE - 1, + (u8 *)req + 1); +} + +static int rtpse_mcu_i2c_smbus_recv(struct rtpse_mcu_ctrl *pse, const struct rtpse_mcu_msg *req, + struct rtpse_mcu_msg *resp) +{ + struct i2c_client *client = to_i2c_client(pse->dev); + int tries, ret; + + for (tries = 0; tries < RTPSE_MCU_I2C_MAX_TRIES; tries++) { + if (tries > 0) + msleep(RTPSE_MCU_I2C_RETRY_MS); + + /* MCU needs 0x00 as command byte for read */ + ret = i2c_smbus_read_i2c_block_data(client, 0x00, + RTPSE_MCU_MSG_SIZE, + (u8 *)resp); + if (ret < 0) + return ret; + if (ret == RTPSE_MCU_MSG_SIZE && rtpse_mcu_resp_is_final(req, resp)) + return 0; + } + + return -ETIMEDOUT; +} + +static const struct rtpse_mcu_transport_ops rtpse_mcu_i2c_smbus_ops = { + .send = rtpse_mcu_i2c_smbus_send, + .recv = rtpse_mcu_i2c_smbus_recv, +}; + +static int rtpse_mcu_i2c_native_send(struct rtpse_mcu_ctrl *pse, const struct rtpse_mcu_msg *req) +{ + struct i2c_client *client = to_i2c_client(pse->dev); + void *buf; + int ret; + + /* i2c_master_send() may DMA, so the buffer must not be on the stack. */ + buf = kmemdup(req, RTPSE_MCU_MSG_SIZE, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + ret = i2c_master_send(client, buf, RTPSE_MCU_MSG_SIZE); + kfree(buf); + if (ret < 0) + return ret; + return ret == RTPSE_MCU_MSG_SIZE ? 0 : -EIO; +} + +static int rtpse_mcu_i2c_native_recv(struct rtpse_mcu_ctrl *pse, const struct rtpse_mcu_msg *req, + struct rtpse_mcu_msg *resp) +{ + struct i2c_client *client = to_i2c_client(pse->dev); + int tries, ret; + u8 *buf; + + /* i2c_master_recv() may DMA, so read into an off-stack buffer. */ + buf = kmalloc(RTPSE_MCU_MSG_SIZE, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + for (tries = 0; tries < RTPSE_MCU_I2C_MAX_TRIES; tries++) { + if (tries > 0) + msleep(RTPSE_MCU_I2C_RETRY_MS); + + ret = i2c_master_recv(client, buf, RTPSE_MCU_MSG_SIZE); + if (ret < 0) + goto out; + if (ret == RTPSE_MCU_MSG_SIZE) { + memcpy(resp, buf, RTPSE_MCU_MSG_SIZE); + if (rtpse_mcu_resp_is_final(req, resp)) { + ret = 0; + goto out; + } + } + } + ret = -ETIMEDOUT; +out: + kfree(buf); + return ret; +} + +static const struct rtpse_mcu_transport_ops rtpse_mcu_i2c_native_ops = { + .send = rtpse_mcu_i2c_native_send, + .recv = rtpse_mcu_i2c_native_recv, +}; + +static int rtpse_mcu_i2c_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + const struct rtpse_mcu_match_data *match; + struct rtpse_mcu_ctrl *pse; + bool use_native; + + match = device_get_match_data(dev); + if (!match) + return dev_err_probe(dev, -ENODEV, "missing match data\n"); + + /* The framing (raw I2C vs SMBus) is carried by the match data. */ + use_native = match->native_i2c; + if (use_native) { + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) + return dev_err_probe(dev, -EOPNOTSUPP, + "plain-I2C MCU protocol requires I2C-capable adapter\n"); + } else { + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_WRITE_I2C_BLOCK | + I2C_FUNC_SMBUS_READ_I2C_BLOCK)) + return dev_err_probe(dev, -EOPNOTSUPP, + "SMBus MCU protocol requires SMBus I2C-block support\n"); + } + + pse = devm_kzalloc(dev, sizeof(*pse), GFP_KERNEL); + if (!pse) + return -ENOMEM; + + pse->dev = dev; + pse->pcdev.owner = THIS_MODULE; + pse->transport = use_native ? &rtpse_mcu_i2c_native_ops : &rtpse_mcu_i2c_smbus_ops; + + return rtpse_mcu_register(pse); +} + +static const struct of_device_id rtpse_mcu_i2c_of_match[] = { + { .compatible = "realtek,pse-mcu-gen1-smbus", .data = &rtpse_mcu_gen1_data }, + { .compatible = "realtek,pse-mcu-gen2-smbus", .data = &rtpse_mcu_gen2_data }, + { .compatible = "realtek,pse-mcu-gen2-i2c", .data = &rtpse_mcu_gen2_i2c_data }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, rtpse_mcu_i2c_of_match); + +static struct i2c_driver rtpse_mcu_i2c_driver = { + .driver = { + .name = "realtek-pse-mcu-i2c", + .of_match_table = rtpse_mcu_i2c_of_match, + }, + .probe = rtpse_mcu_i2c_probe, +}; +module_i2c_driver(rtpse_mcu_i2c_driver); + +MODULE_AUTHOR("Jonas Jelonek "); +MODULE_DESCRIPTION("Realtek PSE MCU driver (I2C transport)"); +MODULE_LICENSE("GPL"); -- 2.53.0