* [PATCH platform-next 0/3] platform/mellanox: Add nvsw core and SPC6 host driver
@ 2026-10-08 6:17 Felix Radensky
2026-10-08 6:17 ` [PATCH platform-next 1/3] platform/mellanox: nvsw-core: Add core driver for Nvidia switches Felix Radensky
` (2 more replies)
0 siblings, 3 replies; 5+ messages in thread
From: Felix Radensky @ 2026-10-08 6:17 UTC (permalink / raw)
To: ilpo.jarvinen, hdegoede, vadimp; +Cc: platform-driver-x86, fradensky
This series adds support for system control and monitoring on Nvidia
SPC6 based switches, through the CPLD/FPGA devices attached to the host
CPU.
Patch 1 adds nvsw-core, which implements the register map setup, the I2C
mux topology and the creation of the mlxreg-* platform devices shared by
all Nvidia switch board drivers. Patch 2 adds nvsw-host-spc6, the first
user of that core, covering the liquid cooled systems, along with
documentation for the register access attributes it exposes. Patch 3
adds the air cooled SPC6 systems, which unlike the liquid cooled ones
carry their own power supplies and fan drawers.
The functionality covered is component resets and reset cause
reporting, mux selection for hardware components, general purpose
registers, LED control, watchdog control, and fan control on the
systems that have fans. The core also creates an mlxreg-hotplug device
for boards that describe one; no SPC6 system does, and that path comes
into use with the BMC driver mentioned below.
Why a new core instead of extending mlx-platform
================================================
The obvious question is why this does not go into mlx-platform.c, which
already serves the existing Mellanox switch generations. There are two
reasons.
The first is that mlx-platform.c cannot reach this hardware. It is
built around a single memory mapped LPC window: it calls
devm_ioport_map() once, hands the resulting base to devm_regmap_init()
with its own ioread8()/iowrite8() callbacks, and every system it
supports is described in terms of offsets into that window. On SPC6 the
CPLD is not on the LPC bus at all, it is an I2C device, and the driver
reaches it with devm_regmap_init_i2c() through a client on the host I2C
tree. Supporting SPC6 in mlx-platform.c would mean giving it a second,
parallel register access path and making its register map setup
conditional, which is a larger and more invasive change than adding a
separate driver.
The same applies beyond SPC6. The identical CPLD register layout is
also managed from an ARM Baseboard Management Controller, where the
device is reached over I2C in the same way but is described in device
tree rather than discovered through DMI. nvsw-core holds only the parts
that do not depend on how the board was found, so the register
definitions and the mlxreg-* device descriptions are shared, and each
board driver supplies its own instantiation. mlx-platform.c is
inherently x86 and DMI bound and cannot serve the BMC side.
The second reason is structural. mlx-platform.c has grown to roughly
8600 lines covering 49 DMI matches, with the per-system description
data and the device creation logic interleaved in a single translation
unit. Adding another switch generation to it makes the file harder to
review and makes it impossible to build support for one system without
carrying the tables for all the others. nvsw splits the two concerns:
the core holds the logic (760 lines) and each board driver holds only
its own description data, so a kernel can be built with just the boards
it needs.
The core is not speculative generality. The SPC6 host driver in this
series is its first user, and we intend to submit a BMC driver and an
NVL7 host driver on top of it once the core has been reviewed.
Why the SPC6 driver is split over two patches
=============================================
Patches 2 and 3 add a single driver, split along a hardware line
rather than chopped into arbitrary pieces. Patch 2 is complete and
useful on its own: it covers the liquid cooled SPC6 systems, which
shipped first. Patch 3 adds the air cooled systems, which arrived
later and expose a different set of CPLD attributes because they carry
their own power supplies and fan drawers.
Most of both patches is register description data, and the two sets
are largely disjoint. Folding them together would give a single patch
of roughly 1800 lines in which two unrelated board descriptions are
interleaved, with nothing in the diff to indicate which attribute
belongs to which system.
Patch 3 is purely additive: it does not modify or remove a single line
introduced by patch 2. The per SKU description struct and the
driver_data based DMI dispatch are introduced in patch 2, so patch 3
adds only the new register definitions, the new board description and
the DMI entry that selects it.
On the shared "mlxplat" platform device name
============================================
nvsw-host-spc6 registers its platform device as "mlxplat", the same
name used by mlx-platform.c. This is deliberate. The mlxreg-io
attributes are a long standing userspace ABI documented in
Documentation/ABI/stable/sysfs-driver-mlxreg-io, and the management
software reads them from
/sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/. Keeping the name
keeps those paths stable across switch generations, so existing tooling
works unchanged on SPC6.
The two drivers cannot collide at runtime: each calls dmi_check_system()
and returns -ENODEV before registering anything, and their DMI tables
are disjoint, so on any given system exactly one of them instantiates.
If you would still rather the new driver used a distinct name, we can
change it, but it would mean a new sysfs path and a userspace ABI break
for the affected systems.
Symbol namespace
================
The two symbols the core exports to its board drivers are exported as
EXPORT_SYMBOL_NS_GPL() in the NVSW namespace, so that the core does not
add anything to the global symbol table and the dependency is explicit
in each consumer via MODULE_IMPORT_NS().
Testing
=======
Built on x86_64 against pdx86/for-next with gcc W=1 and with clang 17
W=1 plus CONFIG_WERROR, both warning free, at every patch in the
series. Checked with sparse v0.6.5-rc1, smatch and the coccinelle
report mode scripts, all clean. checkpatch --strict reports
no errors and no checks. Module linkage and the namespace import were
verified with modpost and modinfo.
Felix Radensky (3):
platform/mellanox: nvsw-core: Add core driver for Nvidia switches
platform/mellanox: nvsw-host-spc6: Add driver for Nvidia SPC6 switches
platform/mellanox: nvsw-host-spc6: Add support for SPC6 air cooled
switches
.../ABI/stable/sysfs-driver-mlxreg-io | 197 +++
drivers/platform/mellanox/Kconfig | 19 +
drivers/platform/mellanox/Makefile | 2 +
drivers/platform/mellanox/nvsw-core.c | 760 +++++++++
drivers/platform/mellanox/nvsw-host-spc6.c | 1496 +++++++++++++++++
drivers/platform/mellanox/nvsw.h | 317 ++++
6 files changed, 2791 insertions(+)
create mode 100644 drivers/platform/mellanox/nvsw-core.c
create mode 100644 drivers/platform/mellanox/nvsw-host-spc6.c
create mode 100644 drivers/platform/mellanox/nvsw.h
base-commit: 98f039265e580291d5e76a11b2072541fbb1c393
--
2.34.1
Recipients (scripts/get_maintainer.pl):
To: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
Hans de Goede <hansg@kernel.org>
Vadim Pasternak <vadimp@nvidia.com>
Cc: platform-driver-x86@vger.kernel.org
linux-kernel@vger.kernel.org
^ permalink raw reply [flat|nested] 5+ messages in thread* [PATCH platform-next 1/3] platform/mellanox: nvsw-core: Add core driver for Nvidia switches 2026-10-08 6:17 [PATCH platform-next 0/3] platform/mellanox: Add nvsw core and SPC6 host driver Felix Radensky @ 2026-10-08 6:17 ` Felix Radensky 2026-10-08 6:17 ` [PATCH platform-next 2/3] platform/mellanox: nvsw-host-spc6: Add driver for Nvidia SPC6 switches Felix Radensky 2026-10-08 6:17 ` [PATCH platform-next 3/3] platform/mellanox: nvsw-host-spc6: Add support for SPC6 air cooled switches Felix Radensky 2 siblings, 0 replies; 5+ messages in thread From: Felix Radensky @ 2026-10-08 6:17 UTC (permalink / raw) To: ilpo.jarvinen, hdegoede, vadimp; +Cc: platform-driver-x86, fradensky The Nvidia switch systems expose system control and monitoring through CPLD/FPGA devices. The register layout and the set of mlxreg-* devices built on top of it are common across switch generations, while the description of which devices exist on a given board is not. Add nvsw-core, which implements the shared part: creation of the register map for the I2C attached CPLD/FPGA, the I2C mux topology, and the registration of the mlxreg-io, mlxreg-hotplug, mlxreg-fan, leds-mlxreg and mlx-wdt platform devices from a board supplied description. Board drivers provide a struct nvsw_core describing their hardware and call nvsw_core_init() and nvsw_core_exit(). Both symbols are exported in the NVSW namespace, so consumers declare the dependency explicitly and the core adds nothing to the global symbol table. The core is selected by the board drivers rather than being directly selectable, since it is not useful on its own. Signed-off-by: Felix Radensky <fradensky@nvidia.com> Reviewed-by: Vadim Pasternak <vadimp@nvidia.com> --- drivers/platform/mellanox/Kconfig | 9 + drivers/platform/mellanox/Makefile | 1 + drivers/platform/mellanox/nvsw-core.c | 707 ++++++++++++++++++++++++++ drivers/platform/mellanox/nvsw.h | 297 +++++++++++ 4 files changed, 1014 insertions(+) create mode 100644 drivers/platform/mellanox/nvsw-core.c create mode 100644 drivers/platform/mellanox/nvsw.h diff --git a/drivers/platform/mellanox/Kconfig b/drivers/platform/mellanox/Kconfig index e3afbe62c7f6..383906a77b6c 100644 --- a/drivers/platform/mellanox/Kconfig +++ b/drivers/platform/mellanox/Kconfig @@ -105,6 +105,15 @@ config MLXBF_PMC to performance monitoring counters within various blocks in the Mellanox BlueField SoC via a sysfs interface. +config NVSW_CORE + tristate + select REGMAP_I2C + help + Shared register map setup and platform device creation for the Nvidia + switch CPLD/FPGA drivers. This symbol has no prompt of its own and is + selected by the drivers that need it, which are built as separate + modules against it. + config NVSW_SN2201 tristate "Nvidia SN2201 platform driver support" depends on HWMON && I2C diff --git a/drivers/platform/mellanox/Makefile b/drivers/platform/mellanox/Makefile index e86723b44c2e..22e40759dd49 100644 --- a/drivers/platform/mellanox/Makefile +++ b/drivers/platform/mellanox/Makefile @@ -11,4 +11,5 @@ obj-$(CONFIG_MLXREG_DPU) += mlxreg-dpu.o obj-$(CONFIG_MLXREG_HOTPLUG) += mlxreg-hotplug.o obj-$(CONFIG_MLXREG_IO) += mlxreg-io.o obj-$(CONFIG_MLXREG_LC) += mlxreg-lc.o +obj-$(CONFIG_NVSW_CORE) += nvsw-core.o obj-$(CONFIG_NVSW_SN2201) += nvsw-sn2201.o diff --git a/drivers/platform/mellanox/nvsw-core.c b/drivers/platform/mellanox/nvsw-core.c new file mode 100644 index 000000000000..81b3e60e8358 --- /dev/null +++ b/drivers/platform/mellanox/nvsw-core.c @@ -0,0 +1,707 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * Nvidia switch platform driver + * + * Copyright (C) 2025-2026 Nvidia Technologies Ltd. + */ + +#include <linux/device.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/platform_data/mlxreg.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include "nvsw.h" + +static bool nvsw_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case NVSW_REG_CPLD1_DBG_LED_OFFSET: + case NVSW_REG_CPLD2_DBG_LED_OFFSET: + case NVSW_REG_CPLD3_DBG_LED_OFFSET: + case NVSW_REG_CPLD4_DBG_LED_OFFSET: + case NVSW_REG_PG1_EVENT_OFFSET: + case NVSW_REG_PG1_MASK_OFFSET: + case NVSW_REG_PG2_EVENT_OFFSET: + case NVSW_REG_PG2_MASK_OFFSET: + case NVSW_REG_PG3_EVENT_OFFSET: + case NVSW_REG_PG3_MASK_OFFSET: + case NVSW_REG_PG4_EVENT_OFFSET: + case NVSW_REG_PG4_MASK_OFFSET: + case NVSW_REG_RESET_GP1_OFFSET: + case NVSW_REG_FIELD_UPGRADE: + case NVSW_REG_GP0_OFFSET: + case NVSW_REG_GP1_OFFSET: + case NVSW_REG_GP7_OFFSET: + case NVSW_REG_UART_BAUD_OFFSET: + case NVSW_REG_PWM_CONTROL_OFFSET: + case NVSW_REG_RESET_GP2_OFFSET: + case NVSW_REG_GP4_OFFSET: + case NVSW_REG_GP5_OFFSET: + case NVSW_REG_GP6_OFFSET: + case NVSW_REG_LED1_OFFSET: + case NVSW_REG_LED5_OFFSET: + case NVSW_REG_LED6_OFFSET: + case NVSW_REG_LED7_OFFSET: + case NVSW_REG_AGGRCO_MASK_OFFSET: + case NVSW_REG_HEALTH_EVENT_OFFSET: + case NVSW_REG_HEALTH_MASK_OFFSET: + case NVSW_REG_AGGR_MASK_OFFSET: + case NVSW_REG_FU_CAP_OFFSET: + case NVSW_REG_BRD4_EVENT_OFFSET: + case NVSW_REG_BRD4_MASK_OFFSET: + case NVSW_REG_AGGRLO_MASK_OFFSET: + case NVSW_REG_BRD1_EVENT_OFFSET: + case NVSW_REG_BRD1_MASK_OFFSET: + case NVSW_REG_BRD2_EVENT_OFFSET: + case NVSW_REG_BRD2_MASK_OFFSET: + case NVSW_REG_ASIC1_EVENT_OFFSET: + case NVSW_REG_ASIC1_MASK_OFFSET: + case NVSW_REG_ASIC2_EVENT_OFFSET: + case NVSW_REG_ASIC2_MASK_OFFSET: + case NVSW_REG_ASIC3_EVENT_OFFSET: + case NVSW_REG_ASIC3_MASK_OFFSET: + case NVSW_REG_ASIC4_EVENT_OFFSET: + case NVSW_REG_ASIC4_MASK_OFFSET: + case NVSW_REG_VR1_ALERT_EVENT_OFFSET: + case NVSW_REG_VR1_ALERT_MASK_OFFSET: + case NVSW_REG_VR2_ALERT_EVENT_OFFSET: + case NVSW_REG_VR2_ALERT_MASK_OFFSET: + case NVSW_REG_PS_DC_OK_EVENT_OFFSET: + case NVSW_REG_PS_DC_OK_MASK_OFFSET: + case NVSW_REG_FAN_EVENT_OFFSET: + case NVSW_REG_FAN_MASK_OFFSET: + case NVSW_REG_PWRB_EVENT_OFFSET: + case NVSW_REG_PWRB_MASK_OFFSET: + case NVSW_REG_EROT_EVENT_OFFSET: + case NVSW_REG_EROT_MASK_OFFSET: + case NVSW_REG_EROT_ERR_EVENT_OFFSET: + case NVSW_REG_EROT_ERR_MASK_OFFSET: + case NVSW_REG_FRU1_EVENT_OFFSET: + case NVSW_REG_FRU1_MASK_OFFSET: + case NVSW_REG_LEAK_EVENT_OFFSET: + case NVSW_REG_LEAK_MASK_OFFSET: + case NVSW_REG_SPI_CHNL_SELECT: + case NVSW_REG_WD2_TMR_OFFSET: + case NVSW_REG_WD2_TLEFT_OFFSET: + case NVSW_REG_WD2_ACT_OFFSET: + case NVSW_REG_WD3_TMR_OFFSET: + case NVSW_REG_WD3_TLEFT_OFFSET: + case NVSW_REG_WD3_ACT_OFFSET: + case NVSW_REG_PWM1_OFFSET: + case NVSW_REG_MUX0_OFFSET: + case NVSW_REG_MUX1_OFFSET: + case NVSW_REG_MUX2_OFFSET: + return true; + } + return false; +} + +static bool nvsw_readable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case NVSW_REG_CPLD1_VER_OFFSET: + case NVSW_REG_CPLD1_PN_OFFSET: + case NVSW_REG_CPLD1_PN1_OFFSET: + case NVSW_REG_CPLD2_VER_OFFSET: + case NVSW_REG_CPLD2_PN_OFFSET: + case NVSW_REG_CPLD2_PN1_OFFSET: + case NVSW_REG_CPLD3_VER_OFFSET: + case NVSW_REG_CPLD3_PN_OFFSET: + case NVSW_REG_CPLD3_PN1_OFFSET: + case NVSW_REG_CPLD4_VER_OFFSET: + case NVSW_REG_CPLD4_PN_OFFSET: + case NVSW_REG_CPLD4_PN1_OFFSET: + case NVSW_REG_CPLD1_DBG_LED_OFFSET: + case NVSW_REG_CPLD2_DBG_LED_OFFSET: + case NVSW_REG_CPLD3_DBG_LED_OFFSET: + case NVSW_REG_CPLD4_DBG_LED_OFFSET: + case NVSW_REG_CPLD7_VER_OFFSET: + case NVSW_REG_PG1_OFFSET: + case NVSW_REG_PG1_EVENT_OFFSET: + case NVSW_REG_PG1_MASK_OFFSET: + case NVSW_REG_PG2_OFFSET: + case NVSW_REG_PG2_EVENT_OFFSET: + case NVSW_REG_PG2_MASK_OFFSET: + case NVSW_REG_PG3_OFFSET: + case NVSW_REG_PG3_EVENT_OFFSET: + case NVSW_REG_PG3_MASK_OFFSET: + case NVSW_REG_PG4_OFFSET: + case NVSW_REG_PG4_EVENT_OFFSET: + case NVSW_REG_PG4_MASK_OFFSET: + case NVSW_REG_CPLD6_VER_OFFSET: + case NVSW_REG_CPLD6_PN_OFFSET: + case NVSW_REG_CPLD6_PN1_OFFSET: + case NVSW_REG_CPLD9_VER_OFFSET: + case NVSW_REG_CPLD10_PN_OFFSET: + case NVSW_REG_CPLD10_PN1_OFFSET: + case NVSW_REG_RESET_GP1_OFFSET: + case NVSW_REG_FIELD_UPGRADE: + case NVSW_REG_SAFE_BIOS_OFFSET: + case NVSW_REG_RESET_CAUSE_OFFSET: + case NVSW_REG_RESET_CAUSE1_OFFSET: + case NVSW_REG_RESET_CAUSE2_OFFSET: + case NVSW_REG_LED1_OFFSET: + case NVSW_REG_LED5_OFFSET: + case NVSW_REG_LED6_OFFSET: + case NVSW_REG_LED7_OFFSET: + case NVSW_REG_CPLD7_PN_OFFSET: + case NVSW_REG_CPLD7_PN1_OFFSET: + case NVSW_REG_RESET_GP2_OFFSET: + case NVSW_REG_GP0_RO_OFFSET: + case NVSW_REG_GP1_RO_OFFSET: + case NVSW_REG_GP2_RO_OFFSET: + case NVSW_REG_GP4_RO_OFFSET: + case NVSW_REG_GP5_RO_OFFSET: + case NVSW_REG_GPCOM0_OFFSET: + case NVSW_REG_GP0_OFFSET: + case NVSW_REG_GP1_OFFSET: + case NVSW_REG_GP7_OFFSET: + case NVSW_REG_UART_BAUD_OFFSET: + case NVSW_REG_PWM_CONTROL_OFFSET: + case NVSW_REG_GP4_OFFSET: + case NVSW_REG_GP5_OFFSET: + case NVSW_REG_GP6_OFFSET: + case NVSW_REG_AGGRCO_OFFSET: + case NVSW_REG_AGGRCO_MASK_OFFSET: + case NVSW_REG_HEALTH_OFFSET: + case NVSW_REG_HEALTH_EVENT_OFFSET: + case NVSW_REG_HEALTH_MASK_OFFSET: + case NVSW_REG_AGGR_OFFSET: + case NVSW_REG_AGGR_MASK_OFFSET: + case NVSW_REG_FU_CAP_OFFSET: + case NVSW_REG_BRD4_OFFSET: + case NVSW_REG_BRD4_EVENT_OFFSET: + case NVSW_REG_BRD4_MASK_OFFSET: + case NVSW_REG_AGGRLO_OFFSET: + case NVSW_REG_AGGRLO_MASK_OFFSET: + case NVSW_REG_BRD1_OFFSET: + case NVSW_REG_BRD1_EVENT_OFFSET: + case NVSW_REG_BRD1_MASK_OFFSET: + case NVSW_REG_BRD2_OFFSET: + case NVSW_REG_BRD2_EVENT_OFFSET: + case NVSW_REG_BRD2_MASK_OFFSET: + case NVSW_REG_ASIC1_HEALTH_OFFSET: + case NVSW_REG_ASIC1_EVENT_OFFSET: + case NVSW_REG_ASIC1_MASK_OFFSET: + case NVSW_REG_ASIC2_HEALTH_OFFSET: + case NVSW_REG_ASIC2_EVENT_OFFSET: + case NVSW_REG_ASIC2_MASK_OFFSET: + case NVSW_REG_ASIC3_HEALTH_OFFSET: + case NVSW_REG_ASIC3_EVENT_OFFSET: + case NVSW_REG_ASIC3_MASK_OFFSET: + case NVSW_REG_ASIC4_HEALTH_OFFSET: + case NVSW_REG_ASIC4_EVENT_OFFSET: + case NVSW_REG_ASIC4_MASK_OFFSET: + case NVSW_REG_VR1_ALERT_OFFSET: + case NVSW_REG_VR1_ALERT_EVENT_OFFSET: + case NVSW_REG_VR1_ALERT_MASK_OFFSET: + case NVSW_REG_VR2_ALERT_OFFSET: + case NVSW_REG_VR2_ALERT_EVENT_OFFSET: + case NVSW_REG_VR2_ALERT_MASK_OFFSET: + case NVSW_REG_PS_DC_OK_OFFSET: + case NVSW_REG_PS_DC_OK_EVENT_OFFSET: + case NVSW_REG_PS_DC_OK_MASK_OFFSET: + case NVSW_REG_CPLD8_PN_OFFSET: + case NVSW_REG_CPLD8_PN1_OFFSET: + case NVSW_REG_CPLD8_VER_OFFSET: + case NVSW_REG_FAN_OFFSET: + case NVSW_REG_FAN_EVENT_OFFSET: + case NVSW_REG_FAN_MASK_OFFSET: + case NVSW_REG_CPLD5_VER_OFFSET: + case NVSW_REG_CPLD5_PN_OFFSET: + case NVSW_REG_CPLD5_PN1_OFFSET: + case NVSW_REG_EROT_OFFSET: + case NVSW_REG_EROT_EVENT_OFFSET: + case NVSW_REG_EROT_MASK_OFFSET: + case NVSW_REG_EROT_ERR_OFFSET: + case NVSW_REG_EROT_ERR_EVENT_OFFSET: + case NVSW_REG_EROT_ERR_MASK_OFFSET: + case NVSW_REG_PWRB_OFFSET: + case NVSW_REG_PWRB_EVENT_OFFSET: + case NVSW_REG_PWRB_MASK_OFFSET: + case NVSW_REG_FRU1_OFFSET: + case NVSW_REG_FRU1_EVENT_OFFSET: + case NVSW_REG_FRU1_MASK_OFFSET: + case NVSW_REG_LEAK_OFFSET: + case NVSW_REG_LEAK_EVENT_OFFSET: + case NVSW_REG_LEAK_MASK_OFFSET: + case NVSW_REG_CPLD1_MVER_OFFSET: + case NVSW_REG_CPLD2_MVER_OFFSET: + case NVSW_REG_CPLD3_MVER_OFFSET: + case NVSW_REG_CPLD4_MVER_OFFSET: + case NVSW_REG_PWM1_OFFSET: + case NVSW_REG_TACHO1_OFFSET: + case NVSW_REG_TACHO2_OFFSET: + case NVSW_REG_TACHO3_OFFSET: + case NVSW_REG_TACHO4_OFFSET: + case NVSW_REG_TACHO5_OFFSET: + case NVSW_REG_TACHO6_OFFSET: + case NVSW_REG_TACHO7_OFFSET: + case NVSW_REG_TACHO8_OFFSET: + case NVSW_REG_TACHO9_OFFSET: + case NVSW_REG_TACHO10_OFFSET: + case NVSW_REG_TACHO11_OFFSET: + case NVSW_REG_TACHO12_OFFSET: + case NVSW_REG_FAN_CAP1_OFFSET: + case NVSW_REG_FAN_DRW_CAP_OFFSET: + case NVSW_REG_TACHO_SPEED_OFFSET: + case NVSW_REG_CONFIG1_OFFSET: + case NVSW_REG_CONFIG2_OFFSET: + case NVSW_REG_CONFIG3_OFFSET: + case NVSW_REG_SPI_CHNL_SELECT: + case NVSW_REG_CPLD5_MVER_OFFSET: + case NVSW_REG_CPLD9_PN_OFFSET: + case NVSW_REG_CPLD9_PN1_OFFSET: + case NVSW_REG_CPLD10_VER_OFFSET: + case NVSW_REG_WD2_TMR_OFFSET: + case NVSW_REG_WD2_TLEFT_OFFSET: + case NVSW_REG_WD2_ACT_OFFSET: + case NVSW_REG_WD3_TMR_OFFSET: + case NVSW_REG_WD3_TLEFT_OFFSET: + case NVSW_REG_WD3_ACT_OFFSET: + case NVSW_REG_CPLD7_MVER_OFFSET: + case NVSW_REG_CPLD8_MVER_OFFSET: + case NVSW_REG_CPLD9_MVER_OFFSET: + case NVSW_REG_CPLD10_MVER_OFFSET: + case NVSW_REG_CPLD6_MVER_OFFSET: + case NVSW_REG_MUX0_OFFSET: + case NVSW_REG_MUX1_OFFSET: + case NVSW_REG_MUX2_OFFSET: + case NVSW_REG_UFM_VERSION_OFFSET: + case NVSW_REG_SYS_CPBLTY0: + return true; + } + return false; +} + +static bool nvsw_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case NVSW_REG_CPLD1_VER_OFFSET: + case NVSW_REG_CPLD1_PN_OFFSET: + case NVSW_REG_CPLD1_PN1_OFFSET: + case NVSW_REG_CPLD2_VER_OFFSET: + case NVSW_REG_CPLD2_PN_OFFSET: + case NVSW_REG_CPLD2_PN1_OFFSET: + case NVSW_REG_CPLD3_VER_OFFSET: + case NVSW_REG_CPLD3_PN_OFFSET: + case NVSW_REG_CPLD3_PN1_OFFSET: + case NVSW_REG_CPLD4_VER_OFFSET: + case NVSW_REG_CPLD4_PN_OFFSET: + case NVSW_REG_CPLD4_PN1_OFFSET: + case NVSW_REG_CPLD1_DBG_LED_OFFSET: + case NVSW_REG_CPLD2_DBG_LED_OFFSET: + case NVSW_REG_CPLD3_DBG_LED_OFFSET: + case NVSW_REG_CPLD4_DBG_LED_OFFSET: + case NVSW_REG_CPLD7_VER_OFFSET: + case NVSW_REG_PG1_OFFSET: + case NVSW_REG_PG1_EVENT_OFFSET: + case NVSW_REG_PG1_MASK_OFFSET: + case NVSW_REG_PG2_OFFSET: + case NVSW_REG_PG2_EVENT_OFFSET: + case NVSW_REG_PG2_MASK_OFFSET: + case NVSW_REG_PG3_OFFSET: + case NVSW_REG_PG3_EVENT_OFFSET: + case NVSW_REG_PG3_MASK_OFFSET: + case NVSW_REG_PG4_OFFSET: + case NVSW_REG_PG4_EVENT_OFFSET: + case NVSW_REG_PG4_MASK_OFFSET: + case NVSW_REG_CPLD6_VER_OFFSET: + case NVSW_REG_CPLD6_PN_OFFSET: + case NVSW_REG_CPLD6_PN1_OFFSET: + case NVSW_REG_CPLD9_VER_OFFSET: + case NVSW_REG_CPLD10_PN_OFFSET: + case NVSW_REG_CPLD10_PN1_OFFSET: + case NVSW_REG_RESET_GP1_OFFSET: + case NVSW_REG_FIELD_UPGRADE: + case NVSW_REG_SAFE_BIOS_OFFSET: + case NVSW_REG_RESET_CAUSE_OFFSET: + case NVSW_REG_RESET_CAUSE1_OFFSET: + case NVSW_REG_RESET_CAUSE2_OFFSET: + case NVSW_REG_LED1_OFFSET: + case NVSW_REG_LED5_OFFSET: + case NVSW_REG_LED6_OFFSET: + case NVSW_REG_LED7_OFFSET: + case NVSW_REG_CPLD7_PN_OFFSET: + case NVSW_REG_CPLD7_PN1_OFFSET: + case NVSW_REG_RESET_GP2_OFFSET: + case NVSW_REG_GP0_RO_OFFSET: + case NVSW_REG_GP1_RO_OFFSET: + case NVSW_REG_GP2_RO_OFFSET: + case NVSW_REG_GPCOM0_OFFSET: + case NVSW_REG_GP0_OFFSET: + case NVSW_REG_GP1_OFFSET: + case NVSW_REG_GP7_OFFSET: + case NVSW_REG_UART_BAUD_OFFSET: + case NVSW_REG_PWM_CONTROL_OFFSET: + case NVSW_REG_GP4_OFFSET: + case NVSW_REG_GP5_OFFSET: + case NVSW_REG_GP6_OFFSET: + case NVSW_REG_AGGRCO_OFFSET: + case NVSW_REG_AGGRCO_MASK_OFFSET: + case NVSW_REG_HEALTH_OFFSET: + case NVSW_REG_HEALTH_EVENT_OFFSET: + case NVSW_REG_HEALTH_MASK_OFFSET: + case NVSW_REG_AGGR_OFFSET: + case NVSW_REG_AGGR_MASK_OFFSET: + case NVSW_REG_FU_CAP_OFFSET: + case NVSW_REG_BRD4_OFFSET: + case NVSW_REG_BRD4_EVENT_OFFSET: + case NVSW_REG_BRD4_MASK_OFFSET: + case NVSW_REG_AGGRLO_OFFSET: + case NVSW_REG_AGGRLO_MASK_OFFSET: + case NVSW_REG_BRD1_OFFSET: + case NVSW_REG_BRD1_EVENT_OFFSET: + case NVSW_REG_BRD1_MASK_OFFSET: + case NVSW_REG_BRD2_OFFSET: + case NVSW_REG_BRD2_EVENT_OFFSET: + case NVSW_REG_BRD2_MASK_OFFSET: + case NVSW_REG_ASIC1_HEALTH_OFFSET: + case NVSW_REG_ASIC1_EVENT_OFFSET: + case NVSW_REG_ASIC1_MASK_OFFSET: + case NVSW_REG_ASIC2_HEALTH_OFFSET: + case NVSW_REG_ASIC2_EVENT_OFFSET: + case NVSW_REG_ASIC2_MASK_OFFSET: + case NVSW_REG_ASIC3_HEALTH_OFFSET: + case NVSW_REG_ASIC3_EVENT_OFFSET: + case NVSW_REG_ASIC3_MASK_OFFSET: + case NVSW_REG_ASIC4_HEALTH_OFFSET: + case NVSW_REG_ASIC4_EVENT_OFFSET: + case NVSW_REG_ASIC4_MASK_OFFSET: + case NVSW_REG_VR1_ALERT_OFFSET: + case NVSW_REG_VR1_ALERT_EVENT_OFFSET: + case NVSW_REG_VR1_ALERT_MASK_OFFSET: + case NVSW_REG_VR2_ALERT_OFFSET: + case NVSW_REG_VR2_ALERT_EVENT_OFFSET: + case NVSW_REG_VR2_ALERT_MASK_OFFSET: + case NVSW_REG_PS_DC_OK_OFFSET: + case NVSW_REG_PS_DC_OK_EVENT_OFFSET: + case NVSW_REG_PS_DC_OK_MASK_OFFSET: + case NVSW_REG_CPLD8_PN_OFFSET: + case NVSW_REG_CPLD8_PN1_OFFSET: + case NVSW_REG_CPLD8_VER_OFFSET: + case NVSW_REG_FAN_OFFSET: + case NVSW_REG_FAN_EVENT_OFFSET: + case NVSW_REG_FAN_MASK_OFFSET: + case NVSW_REG_CPLD5_VER_OFFSET: + case NVSW_REG_CPLD5_PN_OFFSET: + case NVSW_REG_CPLD5_PN1_OFFSET: + case NVSW_REG_EROT_OFFSET: + case NVSW_REG_EROT_EVENT_OFFSET: + case NVSW_REG_EROT_MASK_OFFSET: + case NVSW_REG_EROT_ERR_OFFSET: + case NVSW_REG_EROT_ERR_EVENT_OFFSET: + case NVSW_REG_EROT_ERR_MASK_OFFSET: + case NVSW_REG_PWRB_OFFSET: + case NVSW_REG_PWRB_EVENT_OFFSET: + case NVSW_REG_PWRB_MASK_OFFSET: + case NVSW_REG_FRU1_OFFSET: + case NVSW_REG_FRU1_EVENT_OFFSET: + case NVSW_REG_FRU1_MASK_OFFSET: + case NVSW_REG_LEAK_OFFSET: + case NVSW_REG_LEAK_EVENT_OFFSET: + case NVSW_REG_LEAK_MASK_OFFSET: + case NVSW_REG_GP4_RO_OFFSET: + case NVSW_REG_GP5_RO_OFFSET: + case NVSW_REG_CPLD1_MVER_OFFSET: + case NVSW_REG_CPLD2_MVER_OFFSET: + case NVSW_REG_CPLD3_MVER_OFFSET: + case NVSW_REG_CPLD4_MVER_OFFSET: + case NVSW_REG_PWM1_OFFSET: + case NVSW_REG_TACHO1_OFFSET: + case NVSW_REG_TACHO2_OFFSET: + case NVSW_REG_TACHO3_OFFSET: + case NVSW_REG_TACHO4_OFFSET: + case NVSW_REG_TACHO5_OFFSET: + case NVSW_REG_TACHO6_OFFSET: + case NVSW_REG_TACHO7_OFFSET: + case NVSW_REG_TACHO8_OFFSET: + case NVSW_REG_TACHO9_OFFSET: + case NVSW_REG_TACHO10_OFFSET: + case NVSW_REG_TACHO11_OFFSET: + case NVSW_REG_TACHO12_OFFSET: + case NVSW_REG_FAN_CAP1_OFFSET: + case NVSW_REG_FAN_DRW_CAP_OFFSET: + case NVSW_REG_TACHO_SPEED_OFFSET: + case NVSW_REG_CONFIG1_OFFSET: + case NVSW_REG_CONFIG2_OFFSET: + case NVSW_REG_CONFIG3_OFFSET: + case NVSW_REG_SPI_CHNL_SELECT: + case NVSW_REG_CPLD5_MVER_OFFSET: + case NVSW_REG_CPLD9_PN_OFFSET: + case NVSW_REG_CPLD9_PN1_OFFSET: + case NVSW_REG_CPLD10_VER_OFFSET: + case NVSW_REG_WD2_TMR_OFFSET: + case NVSW_REG_WD2_TLEFT_OFFSET: + case NVSW_REG_WD2_ACT_OFFSET: + case NVSW_REG_CPLD7_MVER_OFFSET: + case NVSW_REG_CPLD8_MVER_OFFSET: + case NVSW_REG_CPLD9_MVER_OFFSET: + case NVSW_REG_CPLD10_MVER_OFFSET: + case NVSW_REG_CPLD6_MVER_OFFSET: + case NVSW_REG_WD3_TMR_OFFSET: + case NVSW_REG_WD3_TLEFT_OFFSET: + case NVSW_REG_WD3_ACT_OFFSET: + case NVSW_REG_MUX0_OFFSET: + case NVSW_REG_MUX1_OFFSET: + case NVSW_REG_MUX2_OFFSET: + case NVSW_REG_UFM_VERSION_OFFSET: + case NVSW_REG_SYS_CPBLTY0: + return true; + } + return false; +} + +/* Configuration for the register map of a device with 2 bytes address space. */ +static const struct reg_default nvsw_core_reg_def[] = { + { NVSW_REG_AGGRCO_MASK_OFFSET, GENMASK(1, 0)}, + { NVSW_REG_PWM_CONTROL_OFFSET, 0x00 }, +}; + +static const struct regmap_config nvsw_regmap_i2c_conf = { + .reg_bits = 16, + .val_bits = 8, + .max_register = NVSW_REG_MAX, + .cache_type = REGCACHE_FLAT, + .reg_defaults = nvsw_core_reg_def, + .num_reg_defaults = ARRAY_SIZE(nvsw_core_reg_def), + .writeable_reg = nvsw_writeable_reg, + .readable_reg = nvsw_readable_reg, + .volatile_reg = nvsw_volatile_reg, +}; + +static int nvsw_core_platform_init(struct nvsw_core *nvsw_core) +{ + int i, err; + + /* Add registers io access driver. */ + if (nvsw_core->regio_data) { + nvsw_core->regio_data->regmap = nvsw_core->regmap; + nvsw_core->regio = + platform_device_register_resndata(nvsw_core->dev, "mlxreg-io", + PLATFORM_DEVID_NONE, NULL, 0, + nvsw_core->regio_data, + sizeof(*nvsw_core->regio_data)); + if (IS_ERR(nvsw_core->regio)) { + err = PTR_ERR(nvsw_core->regio); + nvsw_core->regio = NULL; + dev_err(nvsw_core->dev, "Failed to register mlxreg-io driver\n"); + goto fail_platform_io_register; + } + } + + /* Add FAN driver. */ + if (nvsw_core->fan_data) { + nvsw_core->fan_data->regmap = nvsw_core->regmap; + nvsw_core->fan = platform_device_register_resndata(nvsw_core->dev, "mlxreg-fan", + PLATFORM_DEVID_NONE, NULL, 0, + nvsw_core->fan_data, + sizeof(*nvsw_core->fan_data)); + if (IS_ERR(nvsw_core->fan)) { + err = PTR_ERR(nvsw_core->fan); + nvsw_core->fan = NULL; + dev_err(nvsw_core->dev, "Failed to register mlxreg-fan driver\n"); + goto fail_platform_fan_register; + } + } + + /* Add LED driver. */ + if (nvsw_core->led_data) { + nvsw_core->led_data->regmap = nvsw_core->regmap; + nvsw_core->led = platform_device_register_resndata(nvsw_core->dev, "leds-mlxreg", + PLATFORM_DEVID_NONE, NULL, 0, + nvsw_core->led_data, + sizeof(*nvsw_core->led_data)); + if (IS_ERR(nvsw_core->led)) { + err = PTR_ERR(nvsw_core->led); + nvsw_core->led = NULL; + dev_err(nvsw_core->dev, "Failed to register leds-mlxreg driver\n"); + goto fail_platform_leds_register; + } + } + + /* Add hotplug driver. Hotplug events are reported through the interrupt + * of the I2C device the register map is attached to. + */ + if (nvsw_core->hotplug_data) { + if (!nvsw_core->client || !nvsw_core->client->irq) { + dev_err(nvsw_core->dev, + "Hotplug requires an I2C device with an interrupt\n"); + err = -EINVAL; + goto fail_platform_hotplug_register; + } + nvsw_core->hotplug_data->irq = nvsw_core->client->irq; + nvsw_core->hotplug_data->regmap = nvsw_core->regmap; + nvsw_core->hotplug = + platform_device_register_resndata(nvsw_core->dev, "mlxreg-hotplug", + PLATFORM_DEVID_NONE, NULL, 0, + nvsw_core->hotplug_data, + sizeof(*nvsw_core->hotplug_data)); + if (IS_ERR(nvsw_core->hotplug)) { + err = PTR_ERR(nvsw_core->hotplug); + nvsw_core->hotplug = NULL; + dev_err(nvsw_core->dev, "Failed to register mlxreg-hotplug driver\n"); + goto fail_platform_hotplug_register; + } + } + + for (i = 0; i < ARRAY_SIZE(nvsw_core->wd_data); i++) { + if (nvsw_core->wd_data[i]) { + nvsw_core->wd_data[i]->regmap = nvsw_core->regmap; + nvsw_core->wd[i] = + platform_device_register_resndata(nvsw_core->dev, "mlx-wdt", i, + NULL, 0, nvsw_core->wd_data[i], + sizeof(*nvsw_core->wd_data[i])); + if (IS_ERR(nvsw_core->wd[i])) { + err = PTR_ERR(nvsw_core->wd[i]); + nvsw_core->wd[i] = NULL; + dev_err(nvsw_core->dev, "Failed to register mlx-wdt driver\n"); + goto fail_platform_wd_register; + } + } + } + + return 0; + +fail_platform_wd_register: + while (i--) { + if (nvsw_core->wd[i]) { + platform_device_unregister(nvsw_core->wd[i]); + nvsw_core->wd[i] = NULL; + } + } + if (nvsw_core->hotplug) { + platform_device_unregister(nvsw_core->hotplug); + nvsw_core->hotplug = NULL; + } +fail_platform_hotplug_register: + if (nvsw_core->led) { + platform_device_unregister(nvsw_core->led); + nvsw_core->led = NULL; + } +fail_platform_leds_register: + if (nvsw_core->fan) { + platform_device_unregister(nvsw_core->fan); + nvsw_core->fan = NULL; + } +fail_platform_fan_register: + if (nvsw_core->regio) { + platform_device_unregister(nvsw_core->regio); + nvsw_core->regio = NULL; + } +fail_platform_io_register: + return err; +} + +static void nvsw_core_platform_exit(struct nvsw_core *nvsw_core) +{ + int i; + + for (i = ARRAY_SIZE(nvsw_core->wd) - 1; i >= 0; i--) { + if (nvsw_core->wd[i]) { + platform_device_unregister(nvsw_core->wd[i]); + nvsw_core->wd[i] = NULL; + } + } + if (nvsw_core->hotplug) { + platform_device_unregister(nvsw_core->hotplug); + nvsw_core->hotplug = NULL; + } + if (nvsw_core->led) { + platform_device_unregister(nvsw_core->led); + nvsw_core->led = NULL; + } + if (nvsw_core->fan) { + platform_device_unregister(nvsw_core->fan); + nvsw_core->fan = NULL; + } + if (nvsw_core->regio) { + platform_device_unregister(nvsw_core->regio); + nvsw_core->regio = NULL; + } +} + +static int nvsw_core_mux_topology_init(struct nvsw_core *nvsw_core) +{ + if (!nvsw_core->mux_init) + return 0; + + return nvsw_core->mux_init(nvsw_core); +} + +static void nvsw_core_mux_topology_exit(struct nvsw_core *nvsw_core) +{ + if (nvsw_core->mux_exit) + nvsw_core->mux_exit(nvsw_core); +} + +static int nvsw_core_regmap_init(struct nvsw_core *nvsw_core) +{ + int err; + + if (!nvsw_core->client) + return -EINVAL; + + nvsw_core->regmap = devm_regmap_init_i2c(nvsw_core->client, &nvsw_regmap_i2c_conf); + if (IS_ERR(nvsw_core->regmap)) { + dev_err(nvsw_core->dev, "Failed to create regmap\n"); + return PTR_ERR(nvsw_core->regmap); + } + + /* Sync registers with hardware. */ + regcache_mark_dirty(nvsw_core->regmap); + err = regcache_sync(nvsw_core->regmap); + if (err) { + dev_err(nvsw_core->dev, "Failed to sync regmap\n"); + return err; + } + + /* Set registers default values. */ + if (nvsw_core->set_reg_default) { + err = nvsw_core->set_reg_default(nvsw_core->regmap); + if (err) { + dev_err(nvsw_core->dev, "Failed to set default regmap\n"); + return err; + } + } + + return 0; +} + +int nvsw_core_init(struct nvsw_core *nvsw_core) +{ + int err; + + err = nvsw_core_regmap_init(nvsw_core); + if (err) + return err; + + err = nvsw_core_mux_topology_init(nvsw_core); + if (err) + goto nvsw_core_mux_topology_init_fail; + + err = nvsw_core_platform_init(nvsw_core); + if (err) + goto nvsw_core_platform_init_fail; + + return 0; +nvsw_core_platform_init_fail: + nvsw_core_mux_topology_exit(nvsw_core); +nvsw_core_mux_topology_init_fail: + return err; +} +EXPORT_SYMBOL_NS_GPL(nvsw_core_init, "NVSW"); + +void nvsw_core_exit(struct nvsw_core *nvsw_core) +{ + nvsw_core_platform_exit(nvsw_core); + nvsw_core_mux_topology_exit(nvsw_core); + if (nvsw_core->set_reg_default) + nvsw_core->set_reg_default(nvsw_core->regmap); +} +EXPORT_SYMBOL_NS_GPL(nvsw_core_exit, "NVSW"); + +MODULE_AUTHOR("Vadim Pasternak <vadimp@nvidia.com>"); +MODULE_DESCRIPTION("Nvidia platform core driver"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/platform/mellanox/nvsw.h b/drivers/platform/mellanox/nvsw.h new file mode 100644 index 000000000000..88b16d0a36d4 --- /dev/null +++ b/drivers/platform/mellanox/nvsw.h @@ -0,0 +1,297 @@ +/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) + * + * Nvidia switch platform driver + * + * Copyright (C) 2025-2026 Nvidia Technologies Ltd. + */ + +#ifndef __NVSW_H__ +#define __NVSW_H__ + +#include <linux/bits.h> +#include <linux/types.h> + +struct device; +struct i2c_client; +struct mlxreg_core_hotplug_platform_data; +struct mlxreg_core_platform_data; +struct platform_device; +struct regmap; + +/* I2C bus IO offsets */ +#define NVSW_REG_CPLD1_VER_OFFSET 0x2500 +#define NVSW_REG_CPLD2_VER_OFFSET 0x2501 +#define NVSW_REG_CPLD3_VER_OFFSET 0x2502 +#define NVSW_REG_CPLD4_VER_OFFSET 0x2503 +#define NVSW_REG_CPLD1_PN_OFFSET 0x2504 +#define NVSW_REG_CPLD1_PN1_OFFSET 0x2505 +#define NVSW_REG_CPLD2_PN_OFFSET 0x2506 +#define NVSW_REG_CPLD2_PN1_OFFSET 0x2507 +#define NVSW_REG_CPLD3_PN_OFFSET 0x2508 +#define NVSW_REG_CPLD3_PN1_OFFSET 0x2509 +#define NVSW_REG_CPLD4_PN_OFFSET 0x250a +#define NVSW_REG_CPLD4_PN1_OFFSET 0x250b +#define NVSW_REG_CPLD1_DBG_LED_OFFSET 0x250c +#define NVSW_REG_CPLD2_DBG_LED_OFFSET 0x250d +#define NVSW_REG_CPLD3_DBG_LED_OFFSET 0x250e +#define NVSW_REG_CPLD4_DBG_LED_OFFSET 0x250f +#define NVSW_REG_CPLD7_VER_OFFSET 0x2510 +#define NVSW_REG_PG1_OFFSET 0x2511 +#define NVSW_REG_PG1_EVENT_OFFSET 0x2512 +#define NVSW_REG_PG1_MASK_OFFSET 0x2513 +#define NVSW_REG_PG2_OFFSET 0x2514 +#define NVSW_REG_PG2_EVENT_OFFSET 0x2515 +#define NVSW_REG_PG2_MASK_OFFSET 0x2516 +#define NVSW_REG_RESET_GP1_OFFSET 0x2517 +#define NVSW_REG_RESET_GP2_OFFSET 0x2519 +#define NVSW_REG_RESET_CAUSE_OFFSET 0x251d +#define NVSW_REG_RESET_CAUSE1_OFFSET 0x251e +#define NVSW_REG_RESET_CAUSE2_OFFSET 0x251f +#define NVSW_REG_LED1_OFFSET 0x2520 +#define NVSW_REG_LED5_OFFSET 0x2524 +#define NVSW_REG_LED6_OFFSET 0x2525 +#define NVSW_REG_LED7_OFFSET 0x2526 +#define NVSW_REG_CPLD7_PN_OFFSET 0x2528 +#define NVSW_REG_CPLD7_PN1_OFFSET 0x2529 +#define NVSW_REG_GP0_RO_OFFSET 0x252a +#define NVSW_REG_GP2_RO_OFFSET 0x252b +#define NVSW_REG_GP1_RO_OFFSET 0x252c +#define NVSW_REG_GPCOM0_OFFSET 0x252d +#define NVSW_REG_GP0_OFFSET 0x252e +#define NVSW_REG_GP7_OFFSET 0x252f +#define NVSW_REG_GP1_OFFSET 0x2530 +#define NVSW_REG_UART_BAUD_OFFSET 0x2531 +#define NVSW_REG_GP4_OFFSET 0x2532 +#define NVSW_REG_GP5_RO_OFFSET 0x2533 +#define NVSW_REG_FIELD_UPGRADE 0x2534 +#define NVSW_REG_SAFE_BIOS_OFFSET 0x2535 +#define NVSW_REG_PWM_CONTROL_OFFSET 0x2537 +#define NVSW_REG_GP5_OFFSET 0x2538 +#define NVSW_REG_GP6_OFFSET 0x2539 +#define NVSW_REG_AGGR_OFFSET 0x253a +#define NVSW_REG_AGGR_MASK_OFFSET 0x253b +#define NVSW_REG_FU_CAP_OFFSET 0x253c +#define NVSW_REG_BRD4_OFFSET 0x253d +#define NVSW_REG_BRD4_EVENT_OFFSET 0x253e +#define NVSW_REG_BRD4_MASK_OFFSET 0x253f +#define NVSW_REG_AGGRLO_OFFSET 0x2540 +#define NVSW_REG_AGGRLO_MASK_OFFSET 0x2541 +#define NVSW_REG_AGGRCO_OFFSET 0x2542 +#define NVSW_REG_AGGRCO_MASK_OFFSET 0x2543 +#define NVSW_REG_BRD1_OFFSET 0x2547 +#define NVSW_REG_BRD1_EVENT_OFFSET 0x2548 +#define NVSW_REG_BRD1_MASK_OFFSET 0x2549 +#define NVSW_REG_BRD2_OFFSET 0x254a +#define NVSW_REG_BRD2_EVENT_OFFSET 0x254b +#define NVSW_REG_BRD2_MASK_OFFSET 0x254c +#define NVSW_REG_HEALTH_OFFSET 0x254d +#define NVSW_REG_HEALTH_EVENT_OFFSET 0x254e +#define NVSW_REG_HEALTH_MASK_OFFSET 0x254f +#define NVSW_REG_ASIC1_HEALTH_OFFSET 0x2550 +#define NVSW_REG_ASIC1_EVENT_OFFSET 0x2551 +#define NVSW_REG_ASIC1_MASK_OFFSET 0x2552 +#define NVSW_REG_ASIC2_HEALTH_OFFSET 0x2553 +#define NVSW_REG_ASIC2_EVENT_OFFSET 0x2554 +#define NVSW_REG_ASIC2_MASK_OFFSET 0x2555 +#define NVSW_REG_VR1_ALERT_OFFSET 0x2558 +#define NVSW_REG_VR1_ALERT_EVENT_OFFSET 0x2559 +#define NVSW_REG_VR1_ALERT_MASK_OFFSET 0x255a +#define NVSW_REG_VR2_ALERT_OFFSET 0x255e +#define NVSW_REG_VR2_ALERT_EVENT_OFFSET 0x255f +#define NVSW_REG_VR2_ALERT_MASK_OFFSET 0x2560 +#define NVSW_REG_PS_DC_OK_OFFSET 0x2567 +#define NVSW_REG_PS_DC_OK_EVENT_OFFSET 0x2568 +#define NVSW_REG_PS_DC_OK_MASK_OFFSET 0x2569 +#define NVSW_REG_CPLD8_PN_OFFSET 0x2573 +#define NVSW_REG_CPLD8_PN1_OFFSET 0x2574 +#define NVSW_REG_PG3_OFFSET 0x2576 +#define NVSW_REG_PG3_EVENT_OFFSET 0x2577 +#define NVSW_REG_PG3_MASK_OFFSET 0x2578 +#define NVSW_REG_PG4_OFFSET 0x2579 +#define NVSW_REG_PG4_EVENT_OFFSET 0x257a +#define NVSW_REG_PG4_MASK_OFFSET 0x257b +#define NVSW_REG_CPLD6_VER_OFFSET 0x257c +#define NVSW_REG_CPLD6_PN_OFFSET 0x257d +#define NVSW_REG_CPLD6_PN1_OFFSET 0x257e +#define NVSW_REG_CPLD10_PN_OFFSET 0x2580 +#define NVSW_REG_CPLD10_PN1_OFFSET 0x2581 +#define NVSW_REG_ASIC3_HEALTH_OFFSET 0x2582 +#define NVSW_REG_ASIC3_EVENT_OFFSET 0x2583 +#define NVSW_REG_ASIC3_MASK_OFFSET 0x2584 +#define NVSW_REG_ASIC4_HEALTH_OFFSET 0x2585 +#define NVSW_REG_ASIC4_EVENT_OFFSET 0x2586 +#define NVSW_REG_ASIC4_MASK_OFFSET 0x2587 +#define NVSW_REG_FAN_OFFSET 0x2588 +#define NVSW_REG_FAN_EVENT_OFFSET 0x2589 +#define NVSW_REG_FAN_MASK_OFFSET 0x258a +#define NVSW_REG_CPLD5_VER_OFFSET 0x258e +#define NVSW_REG_CPLD5_PN_OFFSET 0x258f +#define NVSW_REG_CPLD5_PN1_OFFSET 0x2590 +#define NVSW_REG_EROT_OFFSET 0x2591 +#define NVSW_REG_EROT_EVENT_OFFSET 0x2592 +#define NVSW_REG_EROT_MASK_OFFSET 0x2593 +#define NVSW_REG_EROT_ERR_OFFSET 0x2594 +#define NVSW_REG_EROT_ERR_EVENT_OFFSET 0x2595 +#define NVSW_REG_EROT_ERR_MASK_OFFSET 0x2596 +#define NVSW_REG_PWRB_OFFSET 0x2597 +#define NVSW_REG_PWRB_EVENT_OFFSET 0x2598 +#define NVSW_REG_PWRB_MASK_OFFSET 0x2599 +#define NVSW_REG_FRU1_OFFSET 0x25ac +#define NVSW_REG_FRU1_EVENT_OFFSET 0x25ad +#define NVSW_REG_FRU1_MASK_OFFSET 0x25ae +#define NVSW_REG_LEAK_OFFSET 0x25af +#define NVSW_REG_LEAK_EVENT_OFFSET 0x25b0 +#define NVSW_REG_LEAK_MASK_OFFSET 0x25b1 +#define NVSW_REG_CURRENT_TEMP_RO 0x25c0 +#define NVSW_REG_SWITCH_IC_QTY 0x25c1 +#define NVSW_REG_GP4_RO_OFFSET 0x25c2 +#define NVSW_REG_SPI_CHNL_SELECT 0x25c3 +#define NVSW_REG_CPLD5_MVER_OFFSET 0x25c4 +#define NVSW_REG_CPLD8_VER_OFFSET 0x25c8 +#define NVSW_REG_CPLD9_VER_OFFSET 0x25c9 +#define NVSW_REG_CPLD10_VER_OFFSET 0x25ca +#define NVSW_REG_CPLD9_PN_OFFSET 0x25cb +#define NVSW_REG_CPLD9_PN1_OFFSET 0x25cc +#define NVSW_REG_WD2_TMR_OFFSET 0x25cd +#define NVSW_REG_WD2_TLEFT_OFFSET 0x25ce +#define NVSW_REG_WD2_ACT_OFFSET 0x25cf +#define NVSW_REG_WD3_TMR_OFFSET 0x25d1 +#define NVSW_REG_WD3_TLEFT_OFFSET 0x25d2 +#define NVSW_REG_WD3_ACT_OFFSET 0x25d3 +#define NVSW_REG_CPLD7_MVER_OFFSET 0x25d5 +#define NVSW_REG_CPLD8_MVER_OFFSET 0x25d6 +#define NVSW_REG_CPLD9_MVER_OFFSET 0x25d7 +#define NVSW_REG_CPLD10_MVER_OFFSET 0x25d8 +#define NVSW_REG_CPLD6_MVER_OFFSET 0x25d9 +#define NVSW_REG_MUX0_OFFSET 0x25da +#define NVSW_REG_MUX1_OFFSET 0x25db +#define NVSW_REG_MUX2_OFFSET 0x25dc +#define NVSW_REG_CPLD1_MVER_OFFSET 0x25de +#define NVSW_REG_CPLD2_MVER_OFFSET 0x25df +#define NVSW_REG_CPLD3_MVER_OFFSET 0x25e0 +#define NVSW_REG_CPLD4_MVER_OFFSET 0x25e1 +#define NVSW_REG_UFM_VERSION_OFFSET 0x25e2 +#define NVSW_REG_PWM1_OFFSET 0x25e3 +#define NVSW_REG_TACHO1_OFFSET 0x25e4 +#define NVSW_REG_TACHO2_OFFSET 0x25e5 +#define NVSW_REG_TACHO3_OFFSET 0x25e6 +#define NVSW_REG_TACHO4_OFFSET 0x25e7 +#define NVSW_REG_TACHO5_OFFSET 0x25e8 +#define NVSW_REG_TACHO6_OFFSET 0x25e9 +#define NVSW_REG_TACHO7_OFFSET 0x25eb +#define NVSW_REG_TACHO8_OFFSET 0x25ec +#define NVSW_REG_TACHO9_OFFSET 0x25ed +#define NVSW_REG_TACHO10_OFFSET 0x25ee +#define NVSW_REG_TACHO11_OFFSET 0x25ef +#define NVSW_REG_TACHO12_OFFSET 0x25f0 +#define NVSW_REG_FAN_CAP1_OFFSET 0x25f5 +#define NVSW_REG_FAN_DRW_CAP_OFFSET 0x25f7 +#define NVSW_REG_TACHO_SPEED_OFFSET 0x25f8 +#define NVSW_REG_SYS_CPBLTY0 0x25f9 +#define NVSW_REG_CONFIG1_OFFSET 0x25fb +#define NVSW_REG_CONFIG2_OFFSET 0x25fc +#define NVSW_REG_CONFIG3_OFFSET 0x25fd +#define NVSW_REG_MIN 0x2500 +#define NVSW_REG_MAX 0x26ff + +#define NVSW_AGGR_MASK_COMEX BIT(0) +#define NVSW_LOW_AGGR_MASK_I2C BIT(6) +#define NVSW_AGGR_MASK BIT(2) +#define NVSW_LOW_AGGR_MASK_LOW 0xe0 +#define NVSW_REG_FRU1_MASK GENMASK(3, 0) +#define NVSW_LEAK_MASK GENMASK(5, 0) +#define NVSW_SSD_I2C_ALERT_MASK BIT(0) +#define NVSW_WD_EXP_MASK BIT(1) +#define NVSW_5V_USB_MASK BIT(2) +#define NVSW_PCB_TEMP1_MASK BIT(3) +#define NVSW_PCB_TEMP2_MASK BIT(4) +#define NVSW_SGMII_MASK BIT(5) +#define NVSW_SDD_PG_MASK BIT(6) +#define NVSW_LEAK_AGGR_MASK BIT(7) +#define NVSW_EROT_MASK GENMASK(2, 0) +#define NVSW_FAN_NG_MASK GENMASK(6, 0) +#define NVSW_ASIC_MASK GENMASK(1, 0) +#define NVSW_ASIC2_MASK GENMASK(3, 2) +#define NVSW_ASIC3_MASK GENMASK(5, 4) +#define NVSW_ASIC4_MASK GENMASK(7, 6) +#define NVSW_ASICS_MASK GENMASK(7, 0) +#define NVSW_LOW_AGGR_MASK_ASIC1 BIT(0) +#define NVSW_LOW_AGGR_MASK_ASIC2 BIT(1) +#define NVSW_LOW_AGGR_MASK_ASIC3 BIT(2) +#define NVSW_PWR_BUTTON_MASK BIT(0) +#define NVSW_AMB_TEMP_SENSE_MASK BIT(1) +#define NVSW_GRACEFUL_POWER_OFF_MASK BIT(2) +#define NVSW_CPU_POWER_OFF_READY_MASK BIT(3) +#define NVSW_CPU_RESET_MASK BIT(4) +#define NVSW_APML_SMB_ALERT_MASK BIT(5) +#define NVSW_CPU_UNEXP_POWER_OFF_MASK BIT(6) +#define NVSW_UID_PUSH_BUTTON_MASK BIT(7) +#define NVSW_RTC_MASK BIT(0) +#define NVSW_HOT_SWAP_ALERT_MASK BIT(1) +#define NVSW_LED_LO_NIBBLE_MASK GENMASK(7, 4) +#define NVSW_LED_HI_NIBBLE_MASK GENMASK(3, 0) +#define NVSW_UART_SEL_MASK GENMASK(7, 6) +#define NVSW_UART_BAUD_MASK GENMASK(3, 0) +#define NVSW_BIOS_STATUS_MASK GENMASK(3, 1) +#define NVSW_REG_RESET_MASK BIT(1) +#define NVSW_MASTER_MASK BIT(5) +#define NVSW_FU_CAP_MASK GENMASK(1, 0) +#define NVSW_WD_RESET_ACT_MASK GENMASK(7, 1) +#define NVSW_WD_FAN_ACT_MASK (GENMASK(7, 0) & ~BIT(4)) +#define NVSW_WD_TYPE2_TO_MASK 0 +#define NVSW_WD3_DFLT_TIMEOUT 600 +#define NVSW_I2C_CAP_BIT 0x04 +#define NVSW_I2C_CAP_MASK GENMASK(5, NVSW_I2C_CAP_BIT) +#define NVSW_I2C_TRAN_END_L BIT(5) + +#define NVSW_NR_NONE -1 +#define NVSW_MUX_MAX 2 +#define NVSW_WD_MAX 2 + +/** + * struct nvsw_core - device private data + * @dev: device; + * @client: I2C client device; + * @regmap: device register map; + * @regio_data: register access platform data; + * @regio: register access device; + * @hotplug_data: hotplug platform data; + * @hotplug: hotplug device; + * @led: led device; + * @led_data: led platform data; + * @fan: fan device; + * @fan_data: fan platform data; + * @wd: watchdog devices; + * @wd_data: watchdog platform data; + * @set_reg_default: set default registers callback; + * @mux_init: mux initialization callback; + * @mux_exit: mux de-initialization callback; + * @mux: mux devices; + * @mux_num: number of mux devices; + */ +struct nvsw_core { + struct device *dev; + struct i2c_client *client; + struct regmap *regmap; + struct mlxreg_core_platform_data *regio_data; + struct platform_device *regio; + struct mlxreg_core_hotplug_platform_data *hotplug_data; + struct platform_device *hotplug; + struct platform_device *led; + struct mlxreg_core_platform_data *led_data; + struct platform_device *fan; + struct mlxreg_core_platform_data *fan_data; + struct platform_device *wd[NVSW_WD_MAX]; + struct mlxreg_core_platform_data *wd_data[NVSW_WD_MAX]; + int (*set_reg_default)(struct regmap *regmap); + int (*mux_init)(struct nvsw_core *nvsw_core); + void (*mux_exit)(struct nvsw_core *nvsw_core); + struct platform_device *mux[NVSW_MUX_MAX]; + u32 mux_num; +}; + +/* Platform core init/exit functions. */ +int nvsw_core_init(struct nvsw_core *nvsw_core); +void nvsw_core_exit(struct nvsw_core *nvsw_core); + +#endif /* !defined(__NVSW_H__) */ -- 2.34.1 ^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH platform-next 2/3] platform/mellanox: nvsw-host-spc6: Add driver for Nvidia SPC6 switches 2026-10-08 6:17 [PATCH platform-next 0/3] platform/mellanox: Add nvsw core and SPC6 host driver Felix Radensky 2026-10-08 6:17 ` [PATCH platform-next 1/3] platform/mellanox: nvsw-core: Add core driver for Nvidia switches Felix Radensky @ 2026-10-08 6:17 ` Felix Radensky 2026-10-16 11:25 ` Ilpo Järvinen 2026-10-08 6:17 ` [PATCH platform-next 3/3] platform/mellanox: nvsw-host-spc6: Add support for SPC6 air cooled switches Felix Radensky 2 siblings, 1 reply; 5+ messages in thread From: Felix Radensky @ 2026-10-08 6:17 UTC (permalink / raw) To: ilpo.jarvinen, hdegoede, vadimp; +Cc: platform-driver-x86, fradensky The driver allows system control and monitoring of Nvidia liquid cooled Ethernet switches based on the SPC6 ASIC through CPLD/FPGA devices from the host CPU. This control includes: - Various resets handling through sysfs attributes. - Reset cause reports through sysfs attributes. - Mux attributes for hw components selection. - General purpose registers attributes. - LED control. - Watchdog control. These systems carry no power supplies or fan drawers of their own, so the board description covers coolant leakage detection and power distribution board status instead of power supply and fan control. The driver describes the SPC6 hardware and leaves the register map and platform device creation to nvsw-core. The per SKU part of that description is held in struct nvsw_host_spc6_cfg and is selected through the driver_data of the matching DMI entry, so that the board data is chosen by the SKU that was matched rather than by inspecting DMI again, and further SKUs are added by describing them rather than by extending the match callback. Document the register access attributes which are not yet covered by Documentation/ABI/stable/sysfs-driver-mlxreg-io. Signed-off-by: Felix Radensky <fradensky@nvidia.com> Reviewed-by: Vadim Pasternak <vadimp@nvidia.com> --- .../ABI/stable/sysfs-driver-mlxreg-io | 159 +++ drivers/platform/mellanox/Kconfig | 10 + drivers/platform/mellanox/Makefile | 1 + drivers/platform/mellanox/nvsw-host-spc6.c | 904 ++++++++++++++++++ 4 files changed, 1074 insertions(+) create mode 100644 drivers/platform/mellanox/nvsw-host-spc6.c diff --git a/Documentation/ABI/stable/sysfs-driver-mlxreg-io b/Documentation/ABI/stable/sysfs-driver-mlxreg-io index f59461111221..1ed2f071ea62 100644 --- a/Documentation/ABI/stable/sysfs-driver-mlxreg-io +++ b/Documentation/ABI/stable/sysfs-driver-mlxreg-io @@ -813,3 +813,162 @@ Description: These files show status of Unified Fabric Manager upgrade. state. 0 - means upgrade is done, 1 - otherwise. The file is read only. + +What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/cpld6_pn +What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/cpld6_version +What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/cpld6_version_min +What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/cpld7_pn +What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/cpld7_version +What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/cpld7_version_min +What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/cpld8_pn +What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/cpld8_version +What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/cpld8_version_min +What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/cpld9_pn +What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/cpld9_version +What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/cpld9_version_min +What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/cpld10_pn +What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/cpld10_version +What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/cpld10_version_min +Date: September 2026 +KernelVersion: 7.4 +Contact: Felix Radensky <fradensky@nvidia.com> +Description: These files show with which CPLD part numbers, version and minor + versions have been burned the 6-th to the 10-th CPLD devices + equipped on a system. + + The files are read only. + +What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/asic1_reset +What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/asic3_reset +What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/asic4_reset +Date: September 2026 +KernelVersion: 7.4 +Contact: Felix Radensky <fradensky@nvidia.com> +Description: These files allow to reset the first, the third and the fourth + ASIC on a multi-ASIC system. Writing 1 puts the related ASIC + into reset state, writing 0 releases it. + + The files are read/write. + +What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/asic3_health +What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/asic4_health +Date: September 2026 +KernelVersion: 7.4 +Contact: Felix Radensky <fradensky@nvidia.com> +Description: These files show health status of the third and the fourth ASIC + on a multi-ASIC system. The possible values are: + 0 - health failed, 2 - health OK, 3 - ASIC in booting state. + + The files are read only. + +What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/leakage1 +What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/leakage2 +What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/leakage3 +What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/leakage4 +What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/leakage5 +Date: September 2026 +KernelVersion: 7.4 +Contact: Felix Radensky <fradensky@nvidia.com> +Description: These files show the status of the liquid leakage sensors + equipped on a liquid cooled system. Value 1 means that a leak + has been detected by the related sensor, 0 - otherwise. The + number of sensors actually connected depends on the system + type. + + The files are read only. + +What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/pdb1_pwr_status +What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/pdb2_pwr_status +What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/pdb3_pwr_status +What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/pdb4_pwr_status +Date: September 2026 +KernelVersion: 7.4 +Contact: Felix Radensky <fradensky@nvidia.com> +Description: These files show the power status of the power distribution + boards equipped on a system. Value 1 means that the related + board is powered and operational, 0 - otherwise. + + The files are read/write. + +What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/graceful_pwr_off +Date: September 2026 +KernelVersion: 7.4 +Contact: Felix Radensky <fradensky@nvidia.com> +Description: This file allows to initiate a graceful power off sequence and + shows whether such a sequence is in progress. Writing 1 starts + the sequence. + + The file is read/write. + +What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/cpu_shutdown_req +Date: September 2026 +KernelVersion: 7.4 +Contact: Felix Radensky <fradensky@nvidia.com> +Description: This file shows a shutdown request raised towards the CPU. + Value 1 means that a shutdown of the system has been + requested, 0 - otherwise. + + The file is read only. + +What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/cpu_power_off_ready +Date: September 2026 +KernelVersion: 7.4 +Contact: Felix Radensky <fradensky@nvidia.com> +Description: This file allows the CPU to report that it has completed its + shutdown procedures and that the system can be powered down. + Writing 1 signals readiness for power off. + + The file is read/write. + +What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/cpu_mctp_ready +Date: September 2026 +KernelVersion: 7.4 +Contact: Felix Radensky <fradensky@nvidia.com> +Description: This file indicates whether the CPU side of the MCTP link + towards the Board Management Controller is ready. Value 1 + means the link is ready, 0 - otherwise. + + The file is read/write. + +What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/bmc_to_cpu_ctrl +Date: September 2026 +KernelVersion: 7.4 +Contact: Felix Radensky <fradensky@nvidia.com> +Description: This file selects the owner of the shared I2C bus between the + Board Management Controller and the CPU. Value 0 selects the + CPU as the owner, 1 selects the Board Management Controller. + + The file is read/write. + +What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/uart_sel +Date: September 2026 +KernelVersion: 7.4 +Contact: Felix Radensky <fradensky@nvidia.com> +Description: This file selects which of the system consumers is connected to + the shared UART console. + + The file is read/write. + +What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/aux_pwr_cycle +Date: September 2026 +KernelVersion: 7.4 +Contact: Felix Radensky <fradensky@nvidia.com> +Description: This file allows to power cycle the auxiliary power domain of + the system. Writing 1 performs the power cycle. + + The file is write only. + +What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/reset_aux_pwr_or_reload +What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/reset_cpu_thermal +What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/reset_main_51v +What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/reset_mgmt_pwr +Date: September 2026 +KernelVersion: 7.4 +Contact: Felix Radensky <fradensky@nvidia.com> +Description: These files show the system reset cause. Respectively: + auxiliary power failure or CPLD field upgrade, CPU thermal + shutdown, management board 5.1V rail failure and management + board power failure. Value 1 means the related event caused + the last reset. + + The files are read only. diff --git a/drivers/platform/mellanox/Kconfig b/drivers/platform/mellanox/Kconfig index 383906a77b6c..650f7f463c9e 100644 --- a/drivers/platform/mellanox/Kconfig +++ b/drivers/platform/mellanox/Kconfig @@ -114,6 +114,16 @@ config NVSW_CORE selected by the drivers that need it, which are built as separate modules against it. +config NVSW_HOST_SPC6 + tristate "Nvidia SPC6 host CPLD/FPGA Hardware Control and Monitoring" + depends on DMI && HWMON && I2C + select NVSW_CORE + help + Say Y here to include support for the FPGA/CPLD logic by host CPU + equipped on Nvidia SPC6 ethernet switches. + This driver can also be built as a module. If so the module + will be called nvsw-host-spc6. + config NVSW_SN2201 tristate "Nvidia SN2201 platform driver support" depends on HWMON && I2C diff --git a/drivers/platform/mellanox/Makefile b/drivers/platform/mellanox/Makefile index 22e40759dd49..a959d07f1787 100644 --- a/drivers/platform/mellanox/Makefile +++ b/drivers/platform/mellanox/Makefile @@ -12,4 +12,5 @@ obj-$(CONFIG_MLXREG_HOTPLUG) += mlxreg-hotplug.o obj-$(CONFIG_MLXREG_IO) += mlxreg-io.o obj-$(CONFIG_MLXREG_LC) += mlxreg-lc.o obj-$(CONFIG_NVSW_CORE) += nvsw-core.o +obj-$(CONFIG_NVSW_HOST_SPC6) += nvsw-host-spc6.o obj-$(CONFIG_NVSW_SN2201) += nvsw-sn2201.o diff --git a/drivers/platform/mellanox/nvsw-host-spc6.c b/drivers/platform/mellanox/nvsw-host-spc6.c new file mode 100644 index 000000000000..ac3d42d4c70f --- /dev/null +++ b/drivers/platform/mellanox/nvsw-host-spc6.c @@ -0,0 +1,904 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * Nvidia SPC6 host platform driver + * + * Copyright (C) 2025-2026 Nvidia Technologies Ltd. + */ + +#include <linux/device.h> +#include <linux/dmi.h> +#include <linux/i2c.h> +#include <linux/i2c-mux.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/platform_device.h> +#include <linux/platform_data/mlxcpld.h> +#include <linux/platform_data/mlxreg.h> + +#include "nvsw.h" + +#define NVSW_HOST_DEVICE_NAME "mlxplat" + +/* Bus number of mux parent device */ +#define NVSW_I2C_MUX_PARENT_BUS_NR 2 + +/* Bus number of the last host I2C bus */ +#define NVSW_I2C_LAST_HOST_BUS_NR 3 + +/** + * struct nvsw_host_spc6_cfg - per SKU board description + * @regs_io: register access platform data; + * @led: led platform data; + */ +struct nvsw_host_spc6_cfg { + struct mlxreg_core_platform_data *regs_io; + struct mlxreg_core_platform_data *led; +}; + +/* Platform mux channels */ +static int nvsw_host_spc6_mux_channels[] = { + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, + 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, + 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, + 63, 64, 65, 66, 67, +}; + +/* Platform mux data */ +static struct mlxcpld_mux_plat_data nvsw_host_spc6_mux_data[] = { + { + .chan_ids = nvsw_host_spc6_mux_channels, + .num_adaps = ARRAY_SIZE(nvsw_host_spc6_mux_channels), + .sel_reg_addr = NVSW_REG_MUX1_OFFSET, + .reg_size = 2, + }, +}; + +/* Platform mux board info */ +static struct i2c_board_info nvsw_host_spc6_mux_brdinfo = { + I2C_BOARD_INFO("i2c-mux-mlxcpld", 0x33), +}; + +static struct i2c_adapter *nvsw_host_mux_i2c_adapter; +static struct i2c_client *nvsw_host_mux_i2c_client; +static struct mlxcpld_mux_plat_data *nvsw_host_mux_data[NVSW_MUX_MAX]; +static struct i2c_board_info *nvsw_host_mux_brdinfo; +static struct mlxreg_core_platform_data *nvsw_led_data; +static struct mlxreg_core_platform_data *nvsw_regs_io_data; +static struct mlxreg_core_platform_data *nvsw_wd_data[NVSW_WD_MAX]; +static int nvsw_host_mux_num; +static struct platform_device *nvsw_host_dev; + +/* Platform register access data for liquid cooled system */ +static struct mlxreg_core_data nvsw_host_spc6_lc_regs_io_data[] = { + { + .label = "cpld1_version", + .reg = NVSW_REG_CPLD1_VER_OFFSET, + .bit = GENMASK(7, 0), + .mode = 0444, + }, + { + .label = "cpld2_version", + .reg = NVSW_REG_CPLD2_VER_OFFSET, + .bit = GENMASK(7, 0), + .mode = 0444, + }, + { + .label = "cpld3_version", + .reg = NVSW_REG_CPLD3_VER_OFFSET, + .bit = GENMASK(7, 0), + .mode = 0444, + }, + { + .label = "cpld4_version", + .reg = NVSW_REG_CPLD4_VER_OFFSET, + .bit = GENMASK(7, 0), + .mode = 0444, + }, + { + .label = "cpld5_version", + .reg = NVSW_REG_CPLD5_VER_OFFSET, + .bit = GENMASK(7, 0), + .mode = 0444, + }, + { + .label = "cpld6_version", + .reg = NVSW_REG_CPLD6_VER_OFFSET, + .bit = GENMASK(7, 0), + .mode = 0444, + }, + { + .label = "cpld7_version", + .reg = NVSW_REG_CPLD7_VER_OFFSET, + .bit = GENMASK(7, 0), + .mode = 0444, + }, + { + .label = "cpld8_version", + .reg = NVSW_REG_CPLD8_VER_OFFSET, + .bit = GENMASK(7, 0), + .mode = 0444, + }, + { + .label = "cpld9_version", + .reg = NVSW_REG_CPLD9_VER_OFFSET, + .bit = GENMASK(7, 0), + .mode = 0444, + }, + { + .label = "cpld10_version", + .reg = NVSW_REG_CPLD10_VER_OFFSET, + .bit = GENMASK(7, 0), + .mode = 0444, + }, + { + .label = "cpld1_pn", + .reg = NVSW_REG_CPLD1_PN_OFFSET, + .bit = GENMASK(15, 0), + .mode = 0444, + .regnum = 2, + }, + { + .label = "cpld2_pn", + .reg = NVSW_REG_CPLD2_PN_OFFSET, + .bit = GENMASK(15, 0), + .mode = 0444, + .regnum = 2, + }, + { + .label = "cpld3_pn", + .reg = NVSW_REG_CPLD3_PN_OFFSET, + .bit = GENMASK(15, 0), + .mode = 0444, + .regnum = 2, + }, + { + .label = "cpld4_pn", + .reg = NVSW_REG_CPLD4_PN_OFFSET, + .bit = GENMASK(15, 0), + .mode = 0444, + .regnum = 2, + }, + { + .label = "cpld5_pn", + .reg = NVSW_REG_CPLD5_PN_OFFSET, + .bit = GENMASK(15, 0), + .mode = 0444, + .regnum = 2, + }, + { + .label = "cpld6_pn", + .reg = NVSW_REG_CPLD6_PN_OFFSET, + .bit = GENMASK(15, 0), + .mode = 0444, + .regnum = 2, + }, + { + .label = "cpld7_pn", + .reg = NVSW_REG_CPLD7_PN_OFFSET, + .bit = GENMASK(15, 0), + .mode = 0444, + .regnum = 2, + }, + { + .label = "cpld8_pn", + .reg = NVSW_REG_CPLD8_PN_OFFSET, + .bit = GENMASK(15, 0), + .mode = 0444, + .regnum = 2, + }, + { + .label = "cpld9_pn", + .reg = NVSW_REG_CPLD9_PN_OFFSET, + .bit = GENMASK(15, 0), + .mode = 0444, + .regnum = 2, + }, + { + .label = "cpld10_pn", + .reg = NVSW_REG_CPLD10_PN_OFFSET, + .bit = GENMASK(15, 0), + .mode = 0444, + .regnum = 2, + }, + { + .label = "cpld1_version_min", + .reg = NVSW_REG_CPLD1_MVER_OFFSET, + .bit = GENMASK(7, 0), + .mode = 0444, + }, + { + .label = "cpld2_version_min", + .reg = NVSW_REG_CPLD2_MVER_OFFSET, + .bit = GENMASK(7, 0), + .mode = 0444, + }, + { + .label = "cpld3_version_min", + .reg = NVSW_REG_CPLD3_MVER_OFFSET, + .bit = GENMASK(7, 0), + .mode = 0444, + }, + { + .label = "cpld4_version_min", + .reg = NVSW_REG_CPLD4_MVER_OFFSET, + .bit = GENMASK(7, 0), + .mode = 0444, + }, + { + .label = "cpld5_version_min", + .reg = NVSW_REG_CPLD5_MVER_OFFSET, + .bit = GENMASK(7, 0), + .mode = 0444, + }, + { + .label = "cpld6_version_min", + .reg = NVSW_REG_CPLD6_MVER_OFFSET, + .bit = GENMASK(7, 0), + .mode = 0444, + }, + { + .label = "cpld7_version_min", + .reg = NVSW_REG_CPLD7_MVER_OFFSET, + .bit = GENMASK(7, 0), + .mode = 0444, + }, + { + .label = "cpld8_version_min", + .reg = NVSW_REG_CPLD8_MVER_OFFSET, + .bit = GENMASK(7, 0), + .mode = 0444, + }, + { + .label = "cpld9_version_min", + .reg = NVSW_REG_CPLD9_MVER_OFFSET, + .bit = GENMASK(7, 0), + .mode = 0444, + }, + { + .label = "cpld10_version_min", + .reg = NVSW_REG_CPLD10_MVER_OFFSET, + .bit = GENMASK(7, 0), + .mode = 0444, + }, + { + .label = "pwr_converter_prog_en", + .reg = NVSW_REG_GP7_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(0), + .mode = 0644, + }, + { + .label = "graceful_pwr_off", + .reg = NVSW_REG_GP7_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(3), + .mode = 0644, + }, + { + .label = "cpu_mctp_ready", + .reg = NVSW_REG_GP0_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(1), + .mode = 0644, + }, + { + .label = "cpu_shutdown_req", + .reg = NVSW_REG_GP0_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(2), + .mode = 0444, + }, + { + .label = "vpd_wp", + .reg = NVSW_REG_GP0_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(3), + .mode = 0644, + .secured = 1, + }, + { + .label = "pcie_asic_reset_dis", + .reg = NVSW_REG_GP0_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(4), + .mode = 0644, + }, + { + .label = "shutdown_unlock", + .reg = NVSW_REG_GP0_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(5), + .mode = 0644, + }, + { + .label = "cpu_power_off_ready", + .reg = NVSW_REG_GP0_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(6), + .mode = 0644, + }, + { + .label = "pwr_cycle", + .reg = NVSW_REG_GP1_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(2), + .mode = 0200, + }, + { + .label = "pwr_down", + .reg = NVSW_REG_GP1_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(3), + .mode = 0200, + }, + { + .label = "aux_pwr_cycle", + .reg = NVSW_REG_GP1_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(4), + .mode = 0200, + }, + { + .label = "bmc_to_cpu_ctrl", + .reg = NVSW_REG_GP1_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(5), + .mode = 0644, + }, + { + .label = "uart_sel", + .reg = NVSW_REG_GP1_OFFSET, + .mask = NVSW_UART_SEL_MASK, + .bit = 7, + .mode = 0644, + }, + { + .label = "leakage1", + .reg = NVSW_REG_LEAK_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(0), + .mode = 0444, + }, + { + .label = "leakage2", + .reg = NVSW_REG_LEAK_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(1), + .mode = 0444, + }, + { + .label = "leakage3", + .reg = NVSW_REG_LEAK_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(2), + .mode = 0444, + }, + { + .label = "leakage4", + .reg = NVSW_REG_LEAK_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(3), + .mode = 0444, + }, + { + .label = "leakage5", + .reg = NVSW_REG_LEAK_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(4), + .mode = 0444, + }, + { + .label = "asic_reset", + .reg = NVSW_REG_RESET_GP2_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(3), + .mode = 0644, + }, + { + .label = "asic1_reset", + .reg = NVSW_REG_RESET_GP2_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(1), + .mode = 0644, + }, + { + .label = "asic2_reset", + .reg = NVSW_REG_RESET_GP2_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(2), + .mode = 0644, + }, + { + .label = "asic3_reset", + .reg = NVSW_REG_RESET_GP2_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(4), + .mode = 0644, + }, + { + .label = "asic4_reset", + .reg = NVSW_REG_RESET_GP2_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(5), + .mode = 0644, + }, + { + .label = "reset_long_pb", + .reg = NVSW_REG_RESET_CAUSE_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(0), + .mode = 0444, + }, + { + .label = "reset_short_pb", + .reg = NVSW_REG_RESET_CAUSE_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(1), + .mode = 0444, + }, + { + .label = "reset_aux_pwr_or_reload", + .reg = NVSW_REG_RESET_CAUSE_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(2), + .mode = 0444, + }, + { + .label = "reset_swb_dc_dc_pwr_fail", + .reg = NVSW_REG_RESET_CAUSE_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(3), + .mode = 0444, + }, + { + .label = "reset_platform", + .reg = NVSW_REG_RESET_CAUSE_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(4), + .mode = 0444, + }, + { + .label = "reset_swb_wd", + .reg = NVSW_REG_RESET_CAUSE_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(6), + .mode = 0444, + }, + { + .label = "reset_asic_thermal", + .reg = NVSW_REG_RESET_CAUSE_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(7), + .mode = 0444, + }, + { + .label = "reset_soc", + .reg = NVSW_REG_RESET_CAUSE1_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(5), + .mode = 0444, + }, + { + .label = "reset_system", + .reg = NVSW_REG_RESET_CAUSE2_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(1), + .mode = 0444, + }, + { + .label = "reset_sw_pwr_off", + .reg = NVSW_REG_RESET_CAUSE2_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(2), + .mode = 0444, + }, + { + .label = "reset_cpu_thermal", + .reg = NVSW_REG_RESET_CAUSE2_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(3), + .mode = 0444, + }, + { + .label = "reset_pwr_converter_fail", + .reg = NVSW_REG_RESET_CAUSE2_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(5), + .mode = 0444, + }, + { + .label = "reset_main_51v", + .reg = NVSW_REG_RESET_CAUSE2_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(6), + .mode = 0444, + }, + { + .label = "reset_mgmt_pwr", + .reg = NVSW_REG_RESET_CAUSE2_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(7), + .mode = 0444, + }, + { + .label = "jtag_cap", + .reg = NVSW_REG_FU_CAP_OFFSET, + .mask = NVSW_FU_CAP_MASK, + .bit = 1, + .mode = 0444, + }, + { + .label = "jtag_enable", + .reg = NVSW_REG_FIELD_UPGRADE, + .mask = GENMASK(1, 0), + .bit = 1, + .mode = 0644, + }, + { + .label = "asic_health", + .reg = NVSW_REG_ASIC1_HEALTH_OFFSET, + .mask = NVSW_ASIC_MASK, + .bit = 1, + .mode = 0444, + }, + { + .label = "asic2_health", + .reg = NVSW_REG_ASIC1_HEALTH_OFFSET, + .mask = NVSW_ASIC2_MASK, + .bit = 1, + .mode = 0444, + }, + { + .label = "asic3_health", + .reg = NVSW_REG_ASIC1_HEALTH_OFFSET, + .mask = NVSW_ASIC3_MASK, + .bit = 1, + .mode = 0444, + }, + { + .label = "asic4_health", + .reg = NVSW_REG_ASIC1_HEALTH_OFFSET, + .mask = NVSW_ASIC4_MASK, + .bit = 1, + .mode = 0444, + }, + { + .label = "spi_chnl_select", + .reg = NVSW_REG_SPI_CHNL_SELECT, + .mask = GENMASK(7, 0), + .bit = 1, + .mode = 0644, + }, + { + .label = "pdb1_pwr_status", + .reg = NVSW_REG_PS_DC_OK_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(0), + .mode = 0644, + }, + { + .label = "pdb2_pwr_status", + .reg = NVSW_REG_PS_DC_OK_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(1), + .mode = 0644, + }, + { + .label = "pdb3_pwr_status", + .reg = NVSW_REG_PS_DC_OK_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(2), + .mode = 0644, + }, + { + .label = "pdb4_pwr_status", + .reg = NVSW_REG_PS_DC_OK_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(3), + .mode = 0644, + }, +}; + +static struct mlxreg_core_platform_data nvsw_host_spc6_lc_regs_io = { + .data = nvsw_host_spc6_lc_regs_io_data, + .counter = ARRAY_SIZE(nvsw_host_spc6_lc_regs_io_data), +}; + +/* Platform led data for liquid cooled system */ +static struct mlxreg_core_data nvsw_host_spc6_lc_led_data[] = { + { + .label = "status:green", + .reg = NVSW_REG_LED1_OFFSET, + .mask = NVSW_LED_LO_NIBBLE_MASK, + }, + { + .label = "status:amber", + .reg = NVSW_REG_LED1_OFFSET, + .mask = NVSW_LED_LO_NIBBLE_MASK, + }, + { + .label = "power:green", + .reg = NVSW_REG_LED1_OFFSET, + .mask = NVSW_LED_HI_NIBBLE_MASK, + }, + { + .label = "power:amber", + .reg = NVSW_REG_LED1_OFFSET, + .mask = NVSW_LED_HI_NIBBLE_MASK, + }, + { + .label = "uid:blue", + .reg = NVSW_REG_LED5_OFFSET, + .mask = NVSW_LED_LO_NIBBLE_MASK, + }, +}; + +static struct mlxreg_core_platform_data nvsw_host_spc6_lc_led = { + .data = nvsw_host_spc6_lc_led_data, + .counter = ARRAY_SIZE(nvsw_host_spc6_lc_led_data), +}; + +/* Watchdog platform data */ +static struct mlxreg_core_data nvsw_host_spc6_wd_main_regs[] = { + { + .label = "action", + .reg = NVSW_REG_WD2_ACT_OFFSET, + .mask = NVSW_WD_RESET_ACT_MASK, + .bit = 0, + }, + { + .label = "timeout", + .reg = NVSW_REG_WD2_TMR_OFFSET, + .mask = NVSW_WD_TYPE2_TO_MASK, + .health_cntr = NVSW_WD3_DFLT_TIMEOUT, + }, + { + .label = "timeleft", + .reg = NVSW_REG_WD2_TMR_OFFSET, + .mask = NVSW_WD_TYPE2_TO_MASK, + }, + { + .label = "ping", + .reg = NVSW_REG_WD2_ACT_OFFSET, + .mask = NVSW_WD_RESET_ACT_MASK, + .bit = 0, + }, + { + .label = "reset", + .reg = NVSW_REG_RESET_CAUSE_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(6), + .bit = 6, + }, +}; + +static struct mlxreg_core_data nvsw_host_spc6_wd_aux_regs[] = { + { + .label = "action", + .reg = NVSW_REG_WD3_ACT_OFFSET, + .mask = NVSW_WD_FAN_ACT_MASK, + .bit = 4, + }, + { + .label = "timeout", + .reg = NVSW_REG_WD3_TMR_OFFSET, + .mask = NVSW_WD_TYPE2_TO_MASK, + .health_cntr = NVSW_WD3_DFLT_TIMEOUT, + }, + { + .label = "timeleft", + .reg = NVSW_REG_WD3_TMR_OFFSET, + .mask = NVSW_WD_TYPE2_TO_MASK, + }, + { + .label = "ping", + .reg = NVSW_REG_WD3_ACT_OFFSET, + .mask = NVSW_WD_FAN_ACT_MASK, + .bit = 4, + }, +}; + +static struct mlxreg_core_platform_data nvsw_host_spc6_wd_set[] = { + { + .data = nvsw_host_spc6_wd_main_regs, + .counter = ARRAY_SIZE(nvsw_host_spc6_wd_main_regs), + .version = MLX_WDT_TYPE3, + .identity = "mlx-wdt-main", + }, + { + .data = nvsw_host_spc6_wd_aux_regs, + .counter = ARRAY_SIZE(nvsw_host_spc6_wd_aux_regs), + .version = MLX_WDT_TYPE3, + .identity = "mlx-wdt-aux", + }, +}; + +/* Mux init/exit callbacks. */ +static int nvsw_host_spc6_mux_topology_init(struct nvsw_core *nvsw_core) +{ + int i, err; + + /* Create mux infrastructure. */ + for (i = 0; i < nvsw_core->mux_num; i++) { + nvsw_core->mux[i] = + platform_device_register_resndata(&nvsw_host_mux_i2c_client->dev, + "i2c-mux-mlxcpld", i, NULL, + 0, nvsw_host_mux_data[i], + sizeof(*nvsw_host_mux_data[i])); + if (IS_ERR(nvsw_core->mux[i])) { + dev_err(nvsw_core->dev, "Failed to create mux infra\n"); + err = PTR_ERR(nvsw_core->mux[i]); + nvsw_core->mux[i] = NULL; + goto platform_mux_register_fail; + } + } + + return 0; + +platform_mux_register_fail: + while (--i >= 0) { + platform_device_unregister(nvsw_core->mux[i]); + nvsw_core->mux[i] = NULL; + } + + return err; +} + +static void nvsw_host_spc6_mux_topology_exit(struct nvsw_core *nvsw_core) +{ + int i; + + for (i = 0; i < nvsw_core->mux_num; i++) { + if (nvsw_core->mux[i]) { + platform_device_unregister(nvsw_core->mux[i]); + nvsw_core->mux[i] = NULL; + } + } +} + +/* Liquid cooled systems, without power supplies and fans. */ +static const struct nvsw_host_spc6_cfg nvsw_host_spc6_cfg_lc = { + .regs_io = &nvsw_host_spc6_lc_regs_io, + .led = &nvsw_host_spc6_lc_led, +}; + +static int __init nvsw_host_dmi_spc6_switch_matched(const struct dmi_system_id *dmi) +{ + const struct nvsw_host_spc6_cfg *cfg = dmi->driver_data; + int i; + + /* Set system configuration. */ + nvsw_host_mux_num = ARRAY_SIZE(nvsw_host_spc6_mux_data); + for (i = 0; i < nvsw_host_mux_num; i++) + nvsw_host_mux_data[i] = &nvsw_host_spc6_mux_data[i]; + nvsw_host_mux_brdinfo = &nvsw_host_spc6_mux_brdinfo; + nvsw_regs_io_data = cfg->regs_io; + nvsw_led_data = cfg->led; + for (i = 0; i < ARRAY_SIZE(nvsw_host_spc6_wd_set); i++) + nvsw_wd_data[i] = &nvsw_host_spc6_wd_set[i]; + + return 1; +} + +static const struct dmi_system_id nvsw_host_dmi_table[] __initconst = { + { + .callback = nvsw_host_dmi_spc6_switch_matched, + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "VMOD0025"), + DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "HI193"), + }, + .driver_data = (void *)&nvsw_host_spc6_cfg_lc, + }, + { + .callback = nvsw_host_dmi_spc6_switch_matched, + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "VMOD0027"), + DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "HI183"), + }, + .driver_data = (void *)&nvsw_host_spc6_cfg_lc, + }, + { + .callback = nvsw_host_dmi_spc6_switch_matched, + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "VMOD0027"), + DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "HI187"), + }, + .driver_data = (void *)&nvsw_host_spc6_cfg_lc, + }, + { + .callback = nvsw_host_dmi_spc6_switch_matched, + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "VMOD0027"), + DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "HI188"), + }, + .driver_data = (void *)&nvsw_host_spc6_cfg_lc, + }, + { } +}; + +MODULE_DEVICE_TABLE(dmi, nvsw_host_dmi_table); + +static int nvsw_host_probe(struct platform_device *pdev) +{ + struct i2c_adapter *last_host_adapter; + struct nvsw_core *nvsw_core; + int i, err; + + /* Ensure the I2C host driver has finished registering + * all 4 buses (0-3) to prevent the mux from stealing bus 3 + */ + last_host_adapter = i2c_get_adapter(NVSW_I2C_LAST_HOST_BUS_NR); + if (!last_host_adapter) { + dev_dbg(&pdev->dev, + "I2C adapter for bus %d is not ready, deferring probe\n", + NVSW_I2C_LAST_HOST_BUS_NR); + return -EPROBE_DEFER; + } + i2c_put_adapter(last_host_adapter); + + /* Get I2C mux parent adapter */ + nvsw_host_mux_i2c_adapter = i2c_get_adapter(NVSW_I2C_MUX_PARENT_BUS_NR); + if (!nvsw_host_mux_i2c_adapter) { + dev_err(&pdev->dev, + "I2C adapter for bus %d is not available\n", + NVSW_I2C_MUX_PARENT_BUS_NR); + return -ENODEV; + } + + /* Create device at the top of host I2C tree */ + nvsw_host_mux_brdinfo->platform_data = nvsw_host_mux_data; + nvsw_host_mux_i2c_client = i2c_new_client_device(nvsw_host_mux_i2c_adapter, + nvsw_host_mux_brdinfo); + if (IS_ERR(nvsw_host_mux_i2c_client)) { + dev_err(&pdev->dev, + "Failed to create client %s at bus %d at addr 0x%02x\n", + nvsw_host_mux_brdinfo->type, NVSW_I2C_MUX_PARENT_BUS_NR, + nvsw_host_mux_brdinfo->addr); + err = PTR_ERR(nvsw_host_mux_i2c_client); + nvsw_host_mux_i2c_client = NULL; + goto i2c_new_client_device_fail; + } + + nvsw_core = devm_kzalloc(&pdev->dev, sizeof(*nvsw_core), GFP_KERNEL); + if (!nvsw_core) { + err = -ENOMEM; + goto nvsw_host_probe_fail; + } + + /* Set system configuration. */ + nvsw_core->dev = &pdev->dev; + nvsw_core->client = nvsw_host_mux_i2c_client; + nvsw_core->mux_num = nvsw_host_mux_num; + for (i = 0; i < ARRAY_SIZE(nvsw_wd_data); i++) + nvsw_core->wd_data[i] = nvsw_wd_data[i]; + nvsw_core->regio_data = nvsw_regs_io_data; + nvsw_core->led_data = nvsw_led_data; + nvsw_core->mux_init = nvsw_host_spc6_mux_topology_init; + nvsw_core->mux_exit = nvsw_host_spc6_mux_topology_exit; + platform_set_drvdata(pdev, nvsw_core); + + err = nvsw_core_init(nvsw_core); + if (!err) + return 0; + +nvsw_host_probe_fail: + i2c_unregister_device(nvsw_host_mux_i2c_client); + nvsw_host_mux_i2c_client = NULL; +i2c_new_client_device_fail: + i2c_put_adapter(nvsw_host_mux_i2c_adapter); + nvsw_host_mux_i2c_adapter = NULL; + return err; +} + +static void nvsw_host_remove(struct platform_device *pdev) +{ + struct nvsw_core *nvsw_core = platform_get_drvdata(pdev); + + nvsw_core_exit(nvsw_core); + i2c_unregister_device(nvsw_host_mux_i2c_client); + nvsw_host_mux_i2c_client = NULL; + i2c_put_adapter(nvsw_host_mux_i2c_adapter); + nvsw_host_mux_i2c_adapter = NULL; +} + +static struct platform_driver nvsw_host_driver = { + .driver = { + .name = NVSW_HOST_DEVICE_NAME, + .probe_type = PROBE_FORCE_SYNCHRONOUS, + }, + .probe = nvsw_host_probe, + .remove = nvsw_host_remove, +}; + +static int __init nvsw_host_init(void) +{ + int err; + + if (!dmi_check_system(nvsw_host_dmi_table)) + return -ENODEV; + + nvsw_host_dev = platform_device_register_simple(NVSW_HOST_DEVICE_NAME, + PLATFORM_DEVID_NONE, + NULL, 0); + if (IS_ERR(nvsw_host_dev)) + return PTR_ERR(nvsw_host_dev); + + err = platform_driver_register(&nvsw_host_driver); + if (err) + platform_device_unregister(nvsw_host_dev); + + return err; +} +module_init(nvsw_host_init); + +static void __exit nvsw_host_exit(void) +{ + platform_driver_unregister(&nvsw_host_driver); + platform_device_unregister(nvsw_host_dev); +} +module_exit(nvsw_host_exit); + +MODULE_AUTHOR("Felix Radensky <fradensky@nvidia.com>"); +MODULE_DESCRIPTION("Nvidia platform SPC6 driver"); +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_IMPORT_NS("NVSW"); -- 2.34.1 ^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCH platform-next 2/3] platform/mellanox: nvsw-host-spc6: Add driver for Nvidia SPC6 switches 2026-10-08 6:17 ` [PATCH platform-next 2/3] platform/mellanox: nvsw-host-spc6: Add driver for Nvidia SPC6 switches Felix Radensky @ 2026-10-16 11:25 ` Ilpo Järvinen 0 siblings, 0 replies; 5+ messages in thread From: Ilpo Järvinen @ 2026-10-16 11:25 UTC (permalink / raw) To: Felix Radensky; +Cc: hdegoede, vadimp, platform-driver-x86 On Thu, 8 Oct 2026, Felix Radensky wrote: > The driver allows system control and monitoring of Nvidia liquid > cooled Ethernet switches based on the SPC6 ASIC through CPLD/FPGA > devices from the host CPU. > > This control includes: > - Various resets handling through sysfs attributes. > - Reset cause reports through sysfs attributes. > - Mux attributes for hw components selection. > - General purpose registers attributes. > - LED control. > - Watchdog control. > > These systems carry no power supplies or fan drawers of their own, so > the board description covers coolant leakage detection and power > distribution board status instead of power supply and fan control. > > The driver describes the SPC6 hardware and leaves the register map and > platform device creation to nvsw-core. > > The per SKU part of that description is held in struct > nvsw_host_spc6_cfg and is selected through the driver_data of the > matching DMI entry, so that the board data is chosen by the SKU that > was matched rather than by inspecting DMI again, and further SKUs are > added by describing them rather than by extending the match callback. > > Document the register access attributes which are not yet covered by > Documentation/ABI/stable/sysfs-driver-mlxreg-io. > > Signed-off-by: Felix Radensky <fradensky@nvidia.com> > Reviewed-by: Vadim Pasternak <vadimp@nvidia.com> > --- > .../ABI/stable/sysfs-driver-mlxreg-io | 159 +++ > drivers/platform/mellanox/Kconfig | 10 + > drivers/platform/mellanox/Makefile | 1 + > drivers/platform/mellanox/nvsw-host-spc6.c | 904 ++++++++++++++++++ > 4 files changed, 1074 insertions(+) > create mode 100644 drivers/platform/mellanox/nvsw-host-spc6.c > > diff --git a/Documentation/ABI/stable/sysfs-driver-mlxreg-io b/Documentation/ABI/stable/sysfs-driver-mlxreg-io > index f59461111221..1ed2f071ea62 100644 > --- a/Documentation/ABI/stable/sysfs-driver-mlxreg-io > +++ b/Documentation/ABI/stable/sysfs-driver-mlxreg-io > @@ -813,3 +813,162 @@ Description: These files show status of Unified Fabric Manager upgrade. > state. 0 - means upgrade is done, 1 - otherwise. > > The file is read only. > + > +What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/cpld6_pn > +What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/cpld6_version > +What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/cpld6_version_min > +What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/cpld7_pn > +What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/cpld7_version > +What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/cpld7_version_min > +What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/cpld8_pn > +What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/cpld8_version > +What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/cpld8_version_min > +What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/cpld9_pn > +What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/cpld9_version > +What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/cpld9_version_min > +What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/cpld10_pn > +What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/cpld10_version > +What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/cpld10_version_min > +Date: September 2026 > +KernelVersion: 7.4 > +Contact: Felix Radensky <fradensky@nvidia.com> > +Description: These files show with which CPLD part numbers, version and minor > + versions have been burned the 6-th to the 10-th CPLD devices > + equipped on a system. > + > + The files are read only. > + > +What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/asic1_reset > +What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/asic3_reset > +What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/asic4_reset > +Date: September 2026 > +KernelVersion: 7.4 > +Contact: Felix Radensky <fradensky@nvidia.com> > +Description: These files allow to reset the first, the third and the fourth > + ASIC on a multi-ASIC system. Writing 1 puts the related ASIC > + into reset state, writing 0 releases it. > + > + The files are read/write. > + > +What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/asic3_health > +What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/asic4_health > +Date: September 2026 > +KernelVersion: 7.4 > +Contact: Felix Radensky <fradensky@nvidia.com> > +Description: These files show health status of the third and the fourth ASIC > + on a multi-ASIC system. The possible values are: > + 0 - health failed, 2 - health OK, 3 - ASIC in booting state. > + > + The files are read only. > + > +What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/leakage1 > +What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/leakage2 > +What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/leakage3 > +What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/leakage4 > +What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/leakage5 > +Date: September 2026 > +KernelVersion: 7.4 > +Contact: Felix Radensky <fradensky@nvidia.com> > +Description: These files show the status of the liquid leakage sensors > + equipped on a liquid cooled system. Value 1 means that a leak > + has been detected by the related sensor, 0 - otherwise. The > + number of sensors actually connected depends on the system > + type. > + > + The files are read only. > + > +What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/pdb1_pwr_status > +What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/pdb2_pwr_status > +What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/pdb3_pwr_status > +What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/pdb4_pwr_status > +Date: September 2026 > +KernelVersion: 7.4 > +Contact: Felix Radensky <fradensky@nvidia.com> > +Description: These files show the power status of the power distribution > + boards equipped on a system. Value 1 means that the related > + board is powered and operational, 0 - otherwise. > + > + The files are read/write. > + > +What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/graceful_pwr_off > +Date: September 2026 > +KernelVersion: 7.4 > +Contact: Felix Radensky <fradensky@nvidia.com> > +Description: This file allows to initiate a graceful power off sequence and > + shows whether such a sequence is in progress. Writing 1 starts > + the sequence. > + > + The file is read/write. > + > +What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/cpu_shutdown_req > +Date: September 2026 > +KernelVersion: 7.4 > +Contact: Felix Radensky <fradensky@nvidia.com> > +Description: This file shows a shutdown request raised towards the CPU. > + Value 1 means that a shutdown of the system has been > + requested, 0 - otherwise. > + > + The file is read only. > + > +What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/cpu_power_off_ready > +Date: September 2026 > +KernelVersion: 7.4 > +Contact: Felix Radensky <fradensky@nvidia.com> > +Description: This file allows the CPU to report that it has completed its > + shutdown procedures and that the system can be powered down. > + Writing 1 signals readiness for power off. > + > + The file is read/write. > + > +What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/cpu_mctp_ready > +Date: September 2026 > +KernelVersion: 7.4 > +Contact: Felix Radensky <fradensky@nvidia.com> > +Description: This file indicates whether the CPU side of the MCTP link > + towards the Board Management Controller is ready. Value 1 > + means the link is ready, 0 - otherwise. > + > + The file is read/write. > + > +What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/bmc_to_cpu_ctrl > +Date: September 2026 > +KernelVersion: 7.4 > +Contact: Felix Radensky <fradensky@nvidia.com> > +Description: This file selects the owner of the shared I2C bus between the > + Board Management Controller and the CPU. Value 0 selects the > + CPU as the owner, 1 selects the Board Management Controller. > + > + The file is read/write. > + > +What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/uart_sel > +Date: September 2026 > +KernelVersion: 7.4 > +Contact: Felix Radensky <fradensky@nvidia.com> > +Description: This file selects which of the system consumers is connected to > + the shared UART console. > + > + The file is read/write. > + > +What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/aux_pwr_cycle > +Date: September 2026 > +KernelVersion: 7.4 > +Contact: Felix Radensky <fradensky@nvidia.com> > +Description: This file allows to power cycle the auxiliary power domain of > + the system. Writing 1 performs the power cycle. > + > + The file is write only. > + > +What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/reset_aux_pwr_or_reload > +What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/reset_cpu_thermal > +What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/reset_main_51v > +What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/reset_mgmt_pwr > +Date: September 2026 > +KernelVersion: 7.4 > +Contact: Felix Radensky <fradensky@nvidia.com> > +Description: These files show the system reset cause. Respectively: > + auxiliary power failure or CPLD field upgrade, CPU thermal > + shutdown, management board 5.1V rail failure and management > + board power failure. Value 1 means the related event caused > + the last reset. > + > + The files are read only. > diff --git a/drivers/platform/mellanox/Kconfig b/drivers/platform/mellanox/Kconfig > index 383906a77b6c..650f7f463c9e 100644 > --- a/drivers/platform/mellanox/Kconfig > +++ b/drivers/platform/mellanox/Kconfig > @@ -114,6 +114,16 @@ config NVSW_CORE > selected by the drivers that need it, which are built as separate > modules against it. > > +config NVSW_HOST_SPC6 > + tristate "Nvidia SPC6 host CPLD/FPGA Hardware Control and Monitoring" > + depends on DMI && HWMON && I2C > + select NVSW_CORE > + help > + Say Y here to include support for the FPGA/CPLD logic by host CPU > + equipped on Nvidia SPC6 ethernet switches. > + This driver can also be built as a module. If so the module > + will be called nvsw-host-spc6. > + > config NVSW_SN2201 > tristate "Nvidia SN2201 platform driver support" > depends on HWMON && I2C > diff --git a/drivers/platform/mellanox/Makefile b/drivers/platform/mellanox/Makefile > index 22e40759dd49..a959d07f1787 100644 > --- a/drivers/platform/mellanox/Makefile > +++ b/drivers/platform/mellanox/Makefile > @@ -12,4 +12,5 @@ obj-$(CONFIG_MLXREG_HOTPLUG) += mlxreg-hotplug.o > obj-$(CONFIG_MLXREG_IO) += mlxreg-io.o > obj-$(CONFIG_MLXREG_LC) += mlxreg-lc.o > obj-$(CONFIG_NVSW_CORE) += nvsw-core.o > +obj-$(CONFIG_NVSW_HOST_SPC6) += nvsw-host-spc6.o > obj-$(CONFIG_NVSW_SN2201) += nvsw-sn2201.o > diff --git a/drivers/platform/mellanox/nvsw-host-spc6.c b/drivers/platform/mellanox/nvsw-host-spc6.c > new file mode 100644 > index 000000000000..ac3d42d4c70f > --- /dev/null > +++ b/drivers/platform/mellanox/nvsw-host-spc6.c > @@ -0,0 +1,904 @@ > +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) > +/* > + * Nvidia SPC6 host platform driver > + * > + * Copyright (C) 2025-2026 Nvidia Technologies Ltd. > + */ > + > +#include <linux/device.h> > +#include <linux/dmi.h> > +#include <linux/i2c.h> > +#include <linux/i2c-mux.h> > +#include <linux/module.h> > +#include <linux/regmap.h> > +#include <linux/platform_device.h> > +#include <linux/platform_data/mlxcpld.h> > +#include <linux/platform_data/mlxreg.h> > + > +#include "nvsw.h" > + > +#define NVSW_HOST_DEVICE_NAME "mlxplat" > + > +/* Bus number of mux parent device */ > +#define NVSW_I2C_MUX_PARENT_BUS_NR 2 > + > +/* Bus number of the last host I2C bus */ > +#define NVSW_I2C_LAST_HOST_BUS_NR 3 > + > +/** > + * struct nvsw_host_spc6_cfg - per SKU board description > + * @regs_io: register access platform data; > + * @led: led platform data; > + */ > +struct nvsw_host_spc6_cfg { > + struct mlxreg_core_platform_data *regs_io; > + struct mlxreg_core_platform_data *led; > +}; > + > +/* Platform mux channels */ > +static int nvsw_host_spc6_mux_channels[] = { > + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, > + 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, > + 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, > + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, > + 63, 64, 65, 66, 67, > +}; > + > +/* Platform mux data */ > +static struct mlxcpld_mux_plat_data nvsw_host_spc6_mux_data[] = { > + { > + .chan_ids = nvsw_host_spc6_mux_channels, > + .num_adaps = ARRAY_SIZE(nvsw_host_spc6_mux_channels), Add include. > + .sel_reg_addr = NVSW_REG_MUX1_OFFSET, > + .reg_size = 2, > + }, > +}; > + > +/* Platform mux board info */ > +static struct i2c_board_info nvsw_host_spc6_mux_brdinfo = { > + I2C_BOARD_INFO("i2c-mux-mlxcpld", 0x33), > +}; > + > +static struct i2c_adapter *nvsw_host_mux_i2c_adapter; > +static struct i2c_client *nvsw_host_mux_i2c_client; > +static struct mlxcpld_mux_plat_data *nvsw_host_mux_data[NVSW_MUX_MAX]; > +static struct i2c_board_info *nvsw_host_mux_brdinfo; > +static struct mlxreg_core_platform_data *nvsw_led_data; > +static struct mlxreg_core_platform_data *nvsw_regs_io_data; > +static struct mlxreg_core_platform_data *nvsw_wd_data[NVSW_WD_MAX]; > +static int nvsw_host_mux_num; > +static struct platform_device *nvsw_host_dev; > + > +/* Platform register access data for liquid cooled system */ > +static struct mlxreg_core_data nvsw_host_spc6_lc_regs_io_data[] = { > + { > + .label = "cpld1_version", > + .reg = NVSW_REG_CPLD1_VER_OFFSET, > + .bit = GENMASK(7, 0), Add include. > + .mode = 0444, > + }, > + { > + .label = "cpld2_version", > + .reg = NVSW_REG_CPLD2_VER_OFFSET, > + .bit = GENMASK(7, 0), > + .mode = 0444, > + }, > + { > + .label = "cpld3_version", > + .reg = NVSW_REG_CPLD3_VER_OFFSET, > + .bit = GENMASK(7, 0), > + .mode = 0444, > + }, > + { > + .label = "cpld4_version", > + .reg = NVSW_REG_CPLD4_VER_OFFSET, > + .bit = GENMASK(7, 0), > + .mode = 0444, > + }, > + { > + .label = "cpld5_version", > + .reg = NVSW_REG_CPLD5_VER_OFFSET, > + .bit = GENMASK(7, 0), > + .mode = 0444, > + }, > + { > + .label = "cpld6_version", > + .reg = NVSW_REG_CPLD6_VER_OFFSET, > + .bit = GENMASK(7, 0), > + .mode = 0444, > + }, > + { > + .label = "cpld7_version", > + .reg = NVSW_REG_CPLD7_VER_OFFSET, > + .bit = GENMASK(7, 0), > + .mode = 0444, > + }, > + { > + .label = "cpld8_version", > + .reg = NVSW_REG_CPLD8_VER_OFFSET, > + .bit = GENMASK(7, 0), > + .mode = 0444, > + }, > + { > + .label = "cpld9_version", > + .reg = NVSW_REG_CPLD9_VER_OFFSET, > + .bit = GENMASK(7, 0), > + .mode = 0444, > + }, > + { > + .label = "cpld10_version", > + .reg = NVSW_REG_CPLD10_VER_OFFSET, > + .bit = GENMASK(7, 0), > + .mode = 0444, > + }, > + { > + .label = "cpld1_pn", > + .reg = NVSW_REG_CPLD1_PN_OFFSET, > + .bit = GENMASK(15, 0), > + .mode = 0444, > + .regnum = 2, > + }, > + { > + .label = "cpld2_pn", > + .reg = NVSW_REG_CPLD2_PN_OFFSET, > + .bit = GENMASK(15, 0), > + .mode = 0444, > + .regnum = 2, > + }, > + { > + .label = "cpld3_pn", > + .reg = NVSW_REG_CPLD3_PN_OFFSET, > + .bit = GENMASK(15, 0), > + .mode = 0444, > + .regnum = 2, > + }, > + { > + .label = "cpld4_pn", > + .reg = NVSW_REG_CPLD4_PN_OFFSET, > + .bit = GENMASK(15, 0), > + .mode = 0444, > + .regnum = 2, > + }, > + { > + .label = "cpld5_pn", > + .reg = NVSW_REG_CPLD5_PN_OFFSET, > + .bit = GENMASK(15, 0), > + .mode = 0444, > + .regnum = 2, > + }, > + { > + .label = "cpld6_pn", > + .reg = NVSW_REG_CPLD6_PN_OFFSET, > + .bit = GENMASK(15, 0), > + .mode = 0444, > + .regnum = 2, > + }, > + { > + .label = "cpld7_pn", > + .reg = NVSW_REG_CPLD7_PN_OFFSET, > + .bit = GENMASK(15, 0), > + .mode = 0444, > + .regnum = 2, > + }, > + { > + .label = "cpld8_pn", > + .reg = NVSW_REG_CPLD8_PN_OFFSET, > + .bit = GENMASK(15, 0), > + .mode = 0444, > + .regnum = 2, > + }, > + { > + .label = "cpld9_pn", > + .reg = NVSW_REG_CPLD9_PN_OFFSET, > + .bit = GENMASK(15, 0), > + .mode = 0444, > + .regnum = 2, > + }, > + { > + .label = "cpld10_pn", > + .reg = NVSW_REG_CPLD10_PN_OFFSET, > + .bit = GENMASK(15, 0), > + .mode = 0444, > + .regnum = 2, > + }, > + { > + .label = "cpld1_version_min", > + .reg = NVSW_REG_CPLD1_MVER_OFFSET, > + .bit = GENMASK(7, 0), > + .mode = 0444, > + }, > + { > + .label = "cpld2_version_min", > + .reg = NVSW_REG_CPLD2_MVER_OFFSET, > + .bit = GENMASK(7, 0), > + .mode = 0444, > + }, > + { > + .label = "cpld3_version_min", > + .reg = NVSW_REG_CPLD3_MVER_OFFSET, > + .bit = GENMASK(7, 0), > + .mode = 0444, > + }, > + { > + .label = "cpld4_version_min", > + .reg = NVSW_REG_CPLD4_MVER_OFFSET, > + .bit = GENMASK(7, 0), > + .mode = 0444, > + }, > + { > + .label = "cpld5_version_min", > + .reg = NVSW_REG_CPLD5_MVER_OFFSET, > + .bit = GENMASK(7, 0), > + .mode = 0444, > + }, > + { > + .label = "cpld6_version_min", > + .reg = NVSW_REG_CPLD6_MVER_OFFSET, > + .bit = GENMASK(7, 0), > + .mode = 0444, > + }, > + { > + .label = "cpld7_version_min", > + .reg = NVSW_REG_CPLD7_MVER_OFFSET, > + .bit = GENMASK(7, 0), > + .mode = 0444, > + }, > + { > + .label = "cpld8_version_min", > + .reg = NVSW_REG_CPLD8_MVER_OFFSET, > + .bit = GENMASK(7, 0), > + .mode = 0444, > + }, > + { > + .label = "cpld9_version_min", > + .reg = NVSW_REG_CPLD9_MVER_OFFSET, > + .bit = GENMASK(7, 0), > + .mode = 0444, > + }, > + { > + .label = "cpld10_version_min", > + .reg = NVSW_REG_CPLD10_MVER_OFFSET, > + .bit = GENMASK(7, 0), > + .mode = 0444, > + }, > + { > + .label = "pwr_converter_prog_en", > + .reg = NVSW_REG_GP7_OFFSET, > + .mask = GENMASK(7, 0) & ~BIT(0), > + .mode = 0644, > + }, > + { > + .label = "graceful_pwr_off", > + .reg = NVSW_REG_GP7_OFFSET, > + .mask = GENMASK(7, 0) & ~BIT(3), > + .mode = 0644, > + }, > + { > + .label = "cpu_mctp_ready", > + .reg = NVSW_REG_GP0_OFFSET, > + .mask = GENMASK(7, 0) & ~BIT(1), > + .mode = 0644, > + }, > + { > + .label = "cpu_shutdown_req", > + .reg = NVSW_REG_GP0_OFFSET, > + .mask = GENMASK(7, 0) & ~BIT(2), > + .mode = 0444, > + }, > + { > + .label = "vpd_wp", > + .reg = NVSW_REG_GP0_OFFSET, > + .mask = GENMASK(7, 0) & ~BIT(3), > + .mode = 0644, > + .secured = 1, > + }, > + { > + .label = "pcie_asic_reset_dis", > + .reg = NVSW_REG_GP0_OFFSET, > + .mask = GENMASK(7, 0) & ~BIT(4), > + .mode = 0644, > + }, > + { > + .label = "shutdown_unlock", > + .reg = NVSW_REG_GP0_OFFSET, > + .mask = GENMASK(7, 0) & ~BIT(5), > + .mode = 0644, > + }, > + { > + .label = "cpu_power_off_ready", > + .reg = NVSW_REG_GP0_OFFSET, > + .mask = GENMASK(7, 0) & ~BIT(6), > + .mode = 0644, > + }, > + { > + .label = "pwr_cycle", > + .reg = NVSW_REG_GP1_OFFSET, > + .mask = GENMASK(7, 0) & ~BIT(2), > + .mode = 0200, > + }, > + { > + .label = "pwr_down", > + .reg = NVSW_REG_GP1_OFFSET, > + .mask = GENMASK(7, 0) & ~BIT(3), > + .mode = 0200, > + }, > + { > + .label = "aux_pwr_cycle", > + .reg = NVSW_REG_GP1_OFFSET, > + .mask = GENMASK(7, 0) & ~BIT(4), > + .mode = 0200, > + }, > + { > + .label = "bmc_to_cpu_ctrl", > + .reg = NVSW_REG_GP1_OFFSET, > + .mask = GENMASK(7, 0) & ~BIT(5), > + .mode = 0644, > + }, > + { > + .label = "uart_sel", > + .reg = NVSW_REG_GP1_OFFSET, > + .mask = NVSW_UART_SEL_MASK, > + .bit = 7, > + .mode = 0644, > + }, > + { > + .label = "leakage1", > + .reg = NVSW_REG_LEAK_OFFSET, > + .mask = GENMASK(7, 0) & ~BIT(0), > + .mode = 0444, > + }, > + { > + .label = "leakage2", > + .reg = NVSW_REG_LEAK_OFFSET, > + .mask = GENMASK(7, 0) & ~BIT(1), > + .mode = 0444, > + }, > + { > + .label = "leakage3", > + .reg = NVSW_REG_LEAK_OFFSET, > + .mask = GENMASK(7, 0) & ~BIT(2), > + .mode = 0444, > + }, > + { > + .label = "leakage4", > + .reg = NVSW_REG_LEAK_OFFSET, > + .mask = GENMASK(7, 0) & ~BIT(3), > + .mode = 0444, > + }, > + { > + .label = "leakage5", > + .reg = NVSW_REG_LEAK_OFFSET, > + .mask = GENMASK(7, 0) & ~BIT(4), > + .mode = 0444, > + }, > + { > + .label = "asic_reset", > + .reg = NVSW_REG_RESET_GP2_OFFSET, > + .mask = GENMASK(7, 0) & ~BIT(3), > + .mode = 0644, > + }, > + { > + .label = "asic1_reset", > + .reg = NVSW_REG_RESET_GP2_OFFSET, > + .mask = GENMASK(7, 0) & ~BIT(1), > + .mode = 0644, > + }, > + { > + .label = "asic2_reset", > + .reg = NVSW_REG_RESET_GP2_OFFSET, > + .mask = GENMASK(7, 0) & ~BIT(2), > + .mode = 0644, > + }, > + { > + .label = "asic3_reset", > + .reg = NVSW_REG_RESET_GP2_OFFSET, > + .mask = GENMASK(7, 0) & ~BIT(4), > + .mode = 0644, > + }, > + { > + .label = "asic4_reset", > + .reg = NVSW_REG_RESET_GP2_OFFSET, > + .mask = GENMASK(7, 0) & ~BIT(5), > + .mode = 0644, > + }, > + { > + .label = "reset_long_pb", > + .reg = NVSW_REG_RESET_CAUSE_OFFSET, > + .mask = GENMASK(7, 0) & ~BIT(0), > + .mode = 0444, > + }, > + { > + .label = "reset_short_pb", > + .reg = NVSW_REG_RESET_CAUSE_OFFSET, > + .mask = GENMASK(7, 0) & ~BIT(1), > + .mode = 0444, > + }, > + { > + .label = "reset_aux_pwr_or_reload", > + .reg = NVSW_REG_RESET_CAUSE_OFFSET, > + .mask = GENMASK(7, 0) & ~BIT(2), > + .mode = 0444, > + }, > + { > + .label = "reset_swb_dc_dc_pwr_fail", > + .reg = NVSW_REG_RESET_CAUSE_OFFSET, > + .mask = GENMASK(7, 0) & ~BIT(3), > + .mode = 0444, > + }, > + { > + .label = "reset_platform", > + .reg = NVSW_REG_RESET_CAUSE_OFFSET, > + .mask = GENMASK(7, 0) & ~BIT(4), > + .mode = 0444, > + }, > + { > + .label = "reset_swb_wd", > + .reg = NVSW_REG_RESET_CAUSE_OFFSET, > + .mask = GENMASK(7, 0) & ~BIT(6), > + .mode = 0444, > + }, > + { > + .label = "reset_asic_thermal", > + .reg = NVSW_REG_RESET_CAUSE_OFFSET, > + .mask = GENMASK(7, 0) & ~BIT(7), > + .mode = 0444, > + }, > + { > + .label = "reset_soc", > + .reg = NVSW_REG_RESET_CAUSE1_OFFSET, > + .mask = GENMASK(7, 0) & ~BIT(5), > + .mode = 0444, > + }, > + { > + .label = "reset_system", > + .reg = NVSW_REG_RESET_CAUSE2_OFFSET, > + .mask = GENMASK(7, 0) & ~BIT(1), > + .mode = 0444, > + }, > + { > + .label = "reset_sw_pwr_off", > + .reg = NVSW_REG_RESET_CAUSE2_OFFSET, > + .mask = GENMASK(7, 0) & ~BIT(2), > + .mode = 0444, > + }, > + { > + .label = "reset_cpu_thermal", > + .reg = NVSW_REG_RESET_CAUSE2_OFFSET, > + .mask = GENMASK(7, 0) & ~BIT(3), > + .mode = 0444, > + }, > + { > + .label = "reset_pwr_converter_fail", > + .reg = NVSW_REG_RESET_CAUSE2_OFFSET, > + .mask = GENMASK(7, 0) & ~BIT(5), > + .mode = 0444, > + }, > + { > + .label = "reset_main_51v", > + .reg = NVSW_REG_RESET_CAUSE2_OFFSET, > + .mask = GENMASK(7, 0) & ~BIT(6), > + .mode = 0444, > + }, > + { > + .label = "reset_mgmt_pwr", > + .reg = NVSW_REG_RESET_CAUSE2_OFFSET, > + .mask = GENMASK(7, 0) & ~BIT(7), > + .mode = 0444, > + }, > + { > + .label = "jtag_cap", > + .reg = NVSW_REG_FU_CAP_OFFSET, > + .mask = NVSW_FU_CAP_MASK, > + .bit = 1, > + .mode = 0444, > + }, > + { > + .label = "jtag_enable", > + .reg = NVSW_REG_FIELD_UPGRADE, > + .mask = GENMASK(1, 0), > + .bit = 1, > + .mode = 0644, > + }, > + { > + .label = "asic_health", > + .reg = NVSW_REG_ASIC1_HEALTH_OFFSET, > + .mask = NVSW_ASIC_MASK, > + .bit = 1, > + .mode = 0444, > + }, > + { > + .label = "asic2_health", > + .reg = NVSW_REG_ASIC1_HEALTH_OFFSET, > + .mask = NVSW_ASIC2_MASK, > + .bit = 1, > + .mode = 0444, > + }, > + { > + .label = "asic3_health", > + .reg = NVSW_REG_ASIC1_HEALTH_OFFSET, > + .mask = NVSW_ASIC3_MASK, > + .bit = 1, > + .mode = 0444, > + }, > + { > + .label = "asic4_health", > + .reg = NVSW_REG_ASIC1_HEALTH_OFFSET, > + .mask = NVSW_ASIC4_MASK, > + .bit = 1, > + .mode = 0444, > + }, > + { > + .label = "spi_chnl_select", > + .reg = NVSW_REG_SPI_CHNL_SELECT, > + .mask = GENMASK(7, 0), > + .bit = 1, > + .mode = 0644, > + }, > + { > + .label = "pdb1_pwr_status", > + .reg = NVSW_REG_PS_DC_OK_OFFSET, > + .mask = GENMASK(7, 0) & ~BIT(0), > + .mode = 0644, > + }, > + { > + .label = "pdb2_pwr_status", > + .reg = NVSW_REG_PS_DC_OK_OFFSET, > + .mask = GENMASK(7, 0) & ~BIT(1), > + .mode = 0644, > + }, > + { > + .label = "pdb3_pwr_status", > + .reg = NVSW_REG_PS_DC_OK_OFFSET, > + .mask = GENMASK(7, 0) & ~BIT(2), > + .mode = 0644, > + }, > + { > + .label = "pdb4_pwr_status", > + .reg = NVSW_REG_PS_DC_OK_OFFSET, > + .mask = GENMASK(7, 0) & ~BIT(3), > + .mode = 0644, > + }, > +}; > + > +static struct mlxreg_core_platform_data nvsw_host_spc6_lc_regs_io = { > + .data = nvsw_host_spc6_lc_regs_io_data, > + .counter = ARRAY_SIZE(nvsw_host_spc6_lc_regs_io_data), > +}; > + > +/* Platform led data for liquid cooled system */ > +static struct mlxreg_core_data nvsw_host_spc6_lc_led_data[] = { > + { > + .label = "status:green", > + .reg = NVSW_REG_LED1_OFFSET, > + .mask = NVSW_LED_LO_NIBBLE_MASK, > + }, > + { > + .label = "status:amber", > + .reg = NVSW_REG_LED1_OFFSET, > + .mask = NVSW_LED_LO_NIBBLE_MASK, > + }, > + { > + .label = "power:green", > + .reg = NVSW_REG_LED1_OFFSET, > + .mask = NVSW_LED_HI_NIBBLE_MASK, > + }, > + { > + .label = "power:amber", > + .reg = NVSW_REG_LED1_OFFSET, > + .mask = NVSW_LED_HI_NIBBLE_MASK, > + }, > + { > + .label = "uid:blue", > + .reg = NVSW_REG_LED5_OFFSET, > + .mask = NVSW_LED_LO_NIBBLE_MASK, > + }, > +}; > + > +static struct mlxreg_core_platform_data nvsw_host_spc6_lc_led = { > + .data = nvsw_host_spc6_lc_led_data, > + .counter = ARRAY_SIZE(nvsw_host_spc6_lc_led_data), > +}; > + > +/* Watchdog platform data */ > +static struct mlxreg_core_data nvsw_host_spc6_wd_main_regs[] = { > + { > + .label = "action", > + .reg = NVSW_REG_WD2_ACT_OFFSET, > + .mask = NVSW_WD_RESET_ACT_MASK, > + .bit = 0, > + }, > + { > + .label = "timeout", > + .reg = NVSW_REG_WD2_TMR_OFFSET, > + .mask = NVSW_WD_TYPE2_TO_MASK, > + .health_cntr = NVSW_WD3_DFLT_TIMEOUT, > + }, > + { > + .label = "timeleft", > + .reg = NVSW_REG_WD2_TMR_OFFSET, > + .mask = NVSW_WD_TYPE2_TO_MASK, > + }, > + { > + .label = "ping", > + .reg = NVSW_REG_WD2_ACT_OFFSET, > + .mask = NVSW_WD_RESET_ACT_MASK, > + .bit = 0, > + }, > + { > + .label = "reset", > + .reg = NVSW_REG_RESET_CAUSE_OFFSET, > + .mask = GENMASK(7, 0) & ~BIT(6), > + .bit = 6, > + }, > +}; > + > +static struct mlxreg_core_data nvsw_host_spc6_wd_aux_regs[] = { > + { > + .label = "action", > + .reg = NVSW_REG_WD3_ACT_OFFSET, > + .mask = NVSW_WD_FAN_ACT_MASK, > + .bit = 4, > + }, > + { > + .label = "timeout", > + .reg = NVSW_REG_WD3_TMR_OFFSET, > + .mask = NVSW_WD_TYPE2_TO_MASK, > + .health_cntr = NVSW_WD3_DFLT_TIMEOUT, > + }, > + { > + .label = "timeleft", > + .reg = NVSW_REG_WD3_TMR_OFFSET, > + .mask = NVSW_WD_TYPE2_TO_MASK, > + }, > + { > + .label = "ping", > + .reg = NVSW_REG_WD3_ACT_OFFSET, > + .mask = NVSW_WD_FAN_ACT_MASK, > + .bit = 4, > + }, > +}; > + > +static struct mlxreg_core_platform_data nvsw_host_spc6_wd_set[] = { > + { > + .data = nvsw_host_spc6_wd_main_regs, > + .counter = ARRAY_SIZE(nvsw_host_spc6_wd_main_regs), > + .version = MLX_WDT_TYPE3, > + .identity = "mlx-wdt-main", > + }, > + { > + .data = nvsw_host_spc6_wd_aux_regs, > + .counter = ARRAY_SIZE(nvsw_host_spc6_wd_aux_regs), > + .version = MLX_WDT_TYPE3, > + .identity = "mlx-wdt-aux", > + }, > +}; > + > +/* Mux init/exit callbacks. */ > +static int nvsw_host_spc6_mux_topology_init(struct nvsw_core *nvsw_core) > +{ > + int i, err; > + > + /* Create mux infrastructure. */ > + for (i = 0; i < nvsw_core->mux_num; i++) { > + nvsw_core->mux[i] = > + platform_device_register_resndata(&nvsw_host_mux_i2c_client->dev, > + "i2c-mux-mlxcpld", i, NULL, > + 0, nvsw_host_mux_data[i], > + sizeof(*nvsw_host_mux_data[i])); > + if (IS_ERR(nvsw_core->mux[i])) { Add include. > + dev_err(nvsw_core->dev, "Failed to create mux infra\n"); Add include. > + err = PTR_ERR(nvsw_core->mux[i]); > + nvsw_core->mux[i] = NULL; If you end up returning error, doesn't that lead to tearing down everything? So why you need to do these NULL assignment? Please explain/justify why you have so many NULL assignments on your error paths. I suspect they're either unnecessary or some of your decision decisions are not sound (such as calling a generic cleanup function in case of error instead of doing proper rollbacks in-situ). > + goto platform_mux_register_fail; > + } > + } > + > + return 0; > + > +platform_mux_register_fail: > + while (--i >= 0) { > + platform_device_unregister(nvsw_core->mux[i]); > + nvsw_core->mux[i] = NULL; > + } > + > + return err; > +} > + > +static void nvsw_host_spc6_mux_topology_exit(struct nvsw_core *nvsw_core) > +{ > + int i; > + > + for (i = 0; i < nvsw_core->mux_num; i++) { > + if (nvsw_core->mux[i]) { > + platform_device_unregister(nvsw_core->mux[i]); > + nvsw_core->mux[i] = NULL; > + } > + } > +} > + > +/* Liquid cooled systems, without power supplies and fans. */ > +static const struct nvsw_host_spc6_cfg nvsw_host_spc6_cfg_lc = { > + .regs_io = &nvsw_host_spc6_lc_regs_io, > + .led = &nvsw_host_spc6_lc_led, > +}; > + > +static int __init nvsw_host_dmi_spc6_switch_matched(const struct dmi_system_id *dmi) > +{ > + const struct nvsw_host_spc6_cfg *cfg = dmi->driver_data; > + int i; > + > + /* Set system configuration. */ > + nvsw_host_mux_num = ARRAY_SIZE(nvsw_host_spc6_mux_data); > + for (i = 0; i < nvsw_host_mux_num; i++) > + nvsw_host_mux_data[i] = &nvsw_host_spc6_mux_data[i]; > + nvsw_host_mux_brdinfo = &nvsw_host_spc6_mux_brdinfo; > + nvsw_regs_io_data = cfg->regs_io; > + nvsw_led_data = cfg->led; > + for (i = 0; i < ARRAY_SIZE(nvsw_host_spc6_wd_set); i++) > + nvsw_wd_data[i] = &nvsw_host_spc6_wd_set[i]; > + > + return 1; > +} > + > +static const struct dmi_system_id nvsw_host_dmi_table[] __initconst = { > + { > + .callback = nvsw_host_dmi_spc6_switch_matched, > + .matches = { > + DMI_MATCH(DMI_BOARD_NAME, "VMOD0025"), > + DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "HI193"), > + }, > + .driver_data = (void *)&nvsw_host_spc6_cfg_lc, > + }, > + { > + .callback = nvsw_host_dmi_spc6_switch_matched, > + .matches = { > + DMI_MATCH(DMI_BOARD_NAME, "VMOD0027"), > + DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "HI183"), > + }, > + .driver_data = (void *)&nvsw_host_spc6_cfg_lc, > + }, > + { > + .callback = nvsw_host_dmi_spc6_switch_matched, > + .matches = { > + DMI_MATCH(DMI_BOARD_NAME, "VMOD0027"), > + DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "HI187"), > + }, > + .driver_data = (void *)&nvsw_host_spc6_cfg_lc, > + }, > + { > + .callback = nvsw_host_dmi_spc6_switch_matched, > + .matches = { > + DMI_MATCH(DMI_BOARD_NAME, "VMOD0027"), > + DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "HI188"), > + }, > + .driver_data = (void *)&nvsw_host_spc6_cfg_lc, > + }, > + { } > +}; > + > +MODULE_DEVICE_TABLE(dmi, nvsw_host_dmi_table); > + > +static int nvsw_host_probe(struct platform_device *pdev) > +{ > + struct i2c_adapter *last_host_adapter; > + struct nvsw_core *nvsw_core; > + int i, err; > + > + /* Ensure the I2C host driver has finished registering > + * all 4 buses (0-3) to prevent the mux from stealing bus 3 > + */ > + last_host_adapter = i2c_get_adapter(NVSW_I2C_LAST_HOST_BUS_NR); > + if (!last_host_adapter) { > + dev_dbg(&pdev->dev, > + "I2C adapter for bus %d is not ready, deferring probe\n", > + NVSW_I2C_LAST_HOST_BUS_NR); > + return -EPROBE_DEFER; > + } > + i2c_put_adapter(last_host_adapter); > + > + /* Get I2C mux parent adapter */ > + nvsw_host_mux_i2c_adapter = i2c_get_adapter(NVSW_I2C_MUX_PARENT_BUS_NR); > + if (!nvsw_host_mux_i2c_adapter) { > + dev_err(&pdev->dev, > + "I2C adapter for bus %d is not available\n", > + NVSW_I2C_MUX_PARENT_BUS_NR); > + return -ENODEV; > + } > + > + /* Create device at the top of host I2C tree */ > + nvsw_host_mux_brdinfo->platform_data = nvsw_host_mux_data; > + nvsw_host_mux_i2c_client = i2c_new_client_device(nvsw_host_mux_i2c_adapter, > + nvsw_host_mux_brdinfo); > + if (IS_ERR(nvsw_host_mux_i2c_client)) { > + dev_err(&pdev->dev, > + "Failed to create client %s at bus %d at addr 0x%02x\n", > + nvsw_host_mux_brdinfo->type, NVSW_I2C_MUX_PARENT_BUS_NR, > + nvsw_host_mux_brdinfo->addr); > + err = PTR_ERR(nvsw_host_mux_i2c_client); > + nvsw_host_mux_i2c_client = NULL; > + goto i2c_new_client_device_fail; > + } > + > + nvsw_core = devm_kzalloc(&pdev->dev, sizeof(*nvsw_core), GFP_KERNEL); > + if (!nvsw_core) { > + err = -ENOMEM; > + goto nvsw_host_probe_fail; > + } > + > + /* Set system configuration. */ > + nvsw_core->dev = &pdev->dev; > + nvsw_core->client = nvsw_host_mux_i2c_client; > + nvsw_core->mux_num = nvsw_host_mux_num; > + for (i = 0; i < ARRAY_SIZE(nvsw_wd_data); i++) > + nvsw_core->wd_data[i] = nvsw_wd_data[i]; > + nvsw_core->regio_data = nvsw_regs_io_data; > + nvsw_core->led_data = nvsw_led_data; > + nvsw_core->mux_init = nvsw_host_spc6_mux_topology_init; > + nvsw_core->mux_exit = nvsw_host_spc6_mux_topology_exit; > + platform_set_drvdata(pdev, nvsw_core); > + > + err = nvsw_core_init(nvsw_core); > + if (!err) > + return 0; > + > +nvsw_host_probe_fail: > + i2c_unregister_device(nvsw_host_mux_i2c_client); > + nvsw_host_mux_i2c_client = NULL; > +i2c_new_client_device_fail: > + i2c_put_adapter(nvsw_host_mux_i2c_adapter); > + nvsw_host_mux_i2c_adapter = NULL; > + return err; > +} > + > +static void nvsw_host_remove(struct platform_device *pdev) > +{ > + struct nvsw_core *nvsw_core = platform_get_drvdata(pdev); > + > + nvsw_core_exit(nvsw_core); > + i2c_unregister_device(nvsw_host_mux_i2c_client); > + nvsw_host_mux_i2c_client = NULL; > + i2c_put_adapter(nvsw_host_mux_i2c_adapter); > + nvsw_host_mux_i2c_adapter = NULL; > +} > + > +static struct platform_driver nvsw_host_driver = { > + .driver = { > + .name = NVSW_HOST_DEVICE_NAME, > + .probe_type = PROBE_FORCE_SYNCHRONOUS, > + }, > + .probe = nvsw_host_probe, > + .remove = nvsw_host_remove, > +}; > + > +static int __init nvsw_host_init(void) > +{ > + int err; > + > + if (!dmi_check_system(nvsw_host_dmi_table)) > + return -ENODEV; > + > + nvsw_host_dev = platform_device_register_simple(NVSW_HOST_DEVICE_NAME, > + PLATFORM_DEVID_NONE, > + NULL, 0); > + if (IS_ERR(nvsw_host_dev)) > + return PTR_ERR(nvsw_host_dev); > + > + err = platform_driver_register(&nvsw_host_driver); > + if (err) > + platform_device_unregister(nvsw_host_dev); > + > + return err; > +} > +module_init(nvsw_host_init); > + > +static void __exit nvsw_host_exit(void) > +{ > + platform_driver_unregister(&nvsw_host_driver); > + platform_device_unregister(nvsw_host_dev); > +} > +module_exit(nvsw_host_exit); > + > +MODULE_AUTHOR("Felix Radensky <fradensky@nvidia.com>"); > +MODULE_DESCRIPTION("Nvidia platform SPC6 driver"); > +MODULE_LICENSE("Dual BSD/GPL"); > +MODULE_IMPORT_NS("NVSW"); > -- i. ^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH platform-next 3/3] platform/mellanox: nvsw-host-spc6: Add support for SPC6 air cooled switches 2026-10-08 6:17 [PATCH platform-next 0/3] platform/mellanox: Add nvsw core and SPC6 host driver Felix Radensky 2026-10-08 6:17 ` [PATCH platform-next 1/3] platform/mellanox: nvsw-core: Add core driver for Nvidia switches Felix Radensky 2026-10-08 6:17 ` [PATCH platform-next 2/3] platform/mellanox: nvsw-host-spc6: Add driver for Nvidia SPC6 switches Felix Radensky @ 2026-10-08 6:17 ` Felix Radensky 2 siblings, 0 replies; 5+ messages in thread From: Felix Radensky @ 2026-10-08 6:17 UTC (permalink / raw) To: ilpo.jarvinen, hdegoede, vadimp; +Cc: platform-driver-x86, fradensky Add support for air cooled Ethernet switches based on the Nvidia SPC6 ASIC. Unlike the liquid cooled systems already supported by the driver, these carry their own power supplies and fan drawers. They therefore expose a different set of register access attributes: power supply presence, AC and DC status and alarm indications and fan drawer presence, in place of the coolant leakage detection and power distribution board status of the liquid cooled systems. They also come with their own LED layout, adding power supply and per fan drawer LEDs, and with fan control. Describe the system and select its description through the driver_data of the matching DMI entry, so that the board data is chosen by the SKU that was matched rather than by inspecting DMI again. Document the power supply and fan drawer attributes the new system exposes, which are not yet covered by Documentation/ABI/stable/sysfs-driver-mlxreg-io. Signed-off-by: Felix Radensky <fradensky@nvidia.com> Reviewed-by: Vadim Pasternak <vadimp@nvidia.com> --- .../ABI/stable/sysfs-driver-mlxreg-io | 38 ++ drivers/platform/mellanox/nvsw-core.c | 53 ++ drivers/platform/mellanox/nvsw-host-spc6.c | 592 ++++++++++++++++++ drivers/platform/mellanox/nvsw.h | 20 + 4 files changed, 703 insertions(+) diff --git a/Documentation/ABI/stable/sysfs-driver-mlxreg-io b/Documentation/ABI/stable/sysfs-driver-mlxreg-io index 1ed2f071ea62..b114fe95ab43 100644 --- a/Documentation/ABI/stable/sysfs-driver-mlxreg-io +++ b/Documentation/ABI/stable/sysfs-driver-mlxreg-io @@ -972,3 +972,41 @@ Description: These files show the system reset cause. Respectively: the last reset. The files are read only. + +What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/psu_status +What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/psu_ac_status +What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/psu_dc_status +Date: October 2026 +KernelVersion: 7.4 +Contact: Felix Radensky <fradensky@nvidia.com> +Description: These files show the state of the system power supply units, one + bit per unit. Respectively: unit presence, unit AC power good + and unit DC power good. In each file bit value 1 means the + related unit is present, or its power is good. + + The files are read only. + +What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/fan_status +Date: October 2026 +KernelVersion: 7.4 +Contact: Felix Radensky <fradensky@nvidia.com> +Description: This file shows the presence of the system fan drawers, one bit + per drawer. Bit value 1 means the related drawer is present. + + The file is read only. + +What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/psu_event +What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/psu_ac_event +What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/psu_dc_event +What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/fan_event +Date: October 2026 +KernelVersion: 7.4 +Contact: Felix Radensky <fradensky@nvidia.com> +Description: These files show the sticky indication of a change in the + matching status file: power supply unit presence, power supply + unit AC power, power supply unit DC power and fan drawer + presence. Bit value 1 means the related unit changed its state + since the indication was last cleared. Writing 0 to a bit + clears it. + + The files are read/write. diff --git a/drivers/platform/mellanox/nvsw-core.c b/drivers/platform/mellanox/nvsw-core.c index 81b3e60e8358..62055f843232 100644 --- a/drivers/platform/mellanox/nvsw-core.c +++ b/drivers/platform/mellanox/nvsw-core.c @@ -40,6 +40,9 @@ static bool nvsw_writeable_reg(struct device *dev, unsigned int reg) case NVSW_REG_GP5_OFFSET: case NVSW_REG_GP6_OFFSET: case NVSW_REG_LED1_OFFSET: + case NVSW_REG_LED2_OFFSET: + case NVSW_REG_LED3_OFFSET: + case NVSW_REG_LED4_OFFSET: case NVSW_REG_LED5_OFFSET: case NVSW_REG_LED6_OFFSET: case NVSW_REG_LED7_OFFSET: @@ -67,8 +70,14 @@ static bool nvsw_writeable_reg(struct device *dev, unsigned int reg) case NVSW_REG_VR1_ALERT_MASK_OFFSET: case NVSW_REG_VR2_ALERT_EVENT_OFFSET: case NVSW_REG_VR2_ALERT_MASK_OFFSET: + case NVSW_REG_PS_AC_2_EVENT_OFFSET: + case NVSW_REG_PS_AC_2_MASK_OFFSET: case NVSW_REG_PS_DC_OK_EVENT_OFFSET: case NVSW_REG_PS_DC_OK_MASK_OFFSET: + case NVSW_REG_PS_2_EVENT_OFFSET: + case NVSW_REG_PS_2_MASK_OFFSET: + case NVSW_REG_PS_ALARM_EVENT_OFFSET: + case NVSW_REG_PS_ALARM_MASK_OFFSET: case NVSW_REG_FAN_EVENT_OFFSET: case NVSW_REG_FAN_MASK_OFFSET: case NVSW_REG_PWRB_EVENT_OFFSET: @@ -142,6 +151,9 @@ static bool nvsw_readable_reg(struct device *dev, unsigned int reg) case NVSW_REG_RESET_CAUSE1_OFFSET: case NVSW_REG_RESET_CAUSE2_OFFSET: case NVSW_REG_LED1_OFFSET: + case NVSW_REG_LED2_OFFSET: + case NVSW_REG_LED3_OFFSET: + case NVSW_REG_LED4_OFFSET: case NVSW_REG_LED5_OFFSET: case NVSW_REG_LED6_OFFSET: case NVSW_REG_LED7_OFFSET: @@ -199,9 +211,18 @@ static bool nvsw_readable_reg(struct device *dev, unsigned int reg) case NVSW_REG_VR2_ALERT_OFFSET: case NVSW_REG_VR2_ALERT_EVENT_OFFSET: case NVSW_REG_VR2_ALERT_MASK_OFFSET: + case NVSW_REG_PS_AC_2_OFFSET: + case NVSW_REG_PS_AC_2_EVENT_OFFSET: + case NVSW_REG_PS_AC_2_MASK_OFFSET: + case NVSW_REG_PS_2_OFFSET: + case NVSW_REG_PS_2_EVENT_OFFSET: + case NVSW_REG_PS_2_MASK_OFFSET: case NVSW_REG_PS_DC_OK_OFFSET: case NVSW_REG_PS_DC_OK_EVENT_OFFSET: case NVSW_REG_PS_DC_OK_MASK_OFFSET: + case NVSW_REG_PS_ALARM_OFFSET: + case NVSW_REG_PS_ALARM_EVENT_OFFSET: + case NVSW_REG_PS_ALARM_MASK_OFFSET: case NVSW_REG_CPLD8_PN_OFFSET: case NVSW_REG_CPLD8_PN1_OFFSET: case NVSW_REG_CPLD8_VER_OFFSET: @@ -243,6 +264,16 @@ static bool nvsw_readable_reg(struct device *dev, unsigned int reg) case NVSW_REG_TACHO10_OFFSET: case NVSW_REG_TACHO11_OFFSET: case NVSW_REG_TACHO12_OFFSET: + case NVSW_REG_TACHO13_OFFSET: + case NVSW_REG_TACHO14_OFFSET: + case NVSW_REG_TACHO15_OFFSET: + case NVSW_REG_TACHO16_OFFSET: + case NVSW_REG_TACHO17_OFFSET: + case NVSW_REG_TACHO18_OFFSET: + case NVSW_REG_TACHO19_OFFSET: + case NVSW_REG_TACHO20_OFFSET: + case NVSW_REG_CURRENT_TEMP_RO: + case NVSW_REG_SWITCH_IC_QTY: case NVSW_REG_FAN_CAP1_OFFSET: case NVSW_REG_FAN_DRW_CAP_OFFSET: case NVSW_REG_TACHO_SPEED_OFFSET: @@ -320,6 +351,9 @@ static bool nvsw_volatile_reg(struct device *dev, unsigned int reg) case NVSW_REG_RESET_CAUSE1_OFFSET: case NVSW_REG_RESET_CAUSE2_OFFSET: case NVSW_REG_LED1_OFFSET: + case NVSW_REG_LED2_OFFSET: + case NVSW_REG_LED3_OFFSET: + case NVSW_REG_LED4_OFFSET: case NVSW_REG_LED5_OFFSET: case NVSW_REG_LED6_OFFSET: case NVSW_REG_LED7_OFFSET: @@ -375,9 +409,18 @@ static bool nvsw_volatile_reg(struct device *dev, unsigned int reg) case NVSW_REG_VR2_ALERT_OFFSET: case NVSW_REG_VR2_ALERT_EVENT_OFFSET: case NVSW_REG_VR2_ALERT_MASK_OFFSET: + case NVSW_REG_PS_AC_2_OFFSET: + case NVSW_REG_PS_AC_2_EVENT_OFFSET: + case NVSW_REG_PS_AC_2_MASK_OFFSET: + case NVSW_REG_PS_2_OFFSET: + case NVSW_REG_PS_2_EVENT_OFFSET: + case NVSW_REG_PS_2_MASK_OFFSET: case NVSW_REG_PS_DC_OK_OFFSET: case NVSW_REG_PS_DC_OK_EVENT_OFFSET: case NVSW_REG_PS_DC_OK_MASK_OFFSET: + case NVSW_REG_PS_ALARM_OFFSET: + case NVSW_REG_PS_ALARM_EVENT_OFFSET: + case NVSW_REG_PS_ALARM_MASK_OFFSET: case NVSW_REG_CPLD8_PN_OFFSET: case NVSW_REG_CPLD8_PN1_OFFSET: case NVSW_REG_CPLD8_VER_OFFSET: @@ -404,6 +447,8 @@ static bool nvsw_volatile_reg(struct device *dev, unsigned int reg) case NVSW_REG_LEAK_MASK_OFFSET: case NVSW_REG_GP4_RO_OFFSET: case NVSW_REG_GP5_RO_OFFSET: + case NVSW_REG_CURRENT_TEMP_RO: + case NVSW_REG_SWITCH_IC_QTY: case NVSW_REG_CPLD1_MVER_OFFSET: case NVSW_REG_CPLD2_MVER_OFFSET: case NVSW_REG_CPLD3_MVER_OFFSET: @@ -421,6 +466,14 @@ static bool nvsw_volatile_reg(struct device *dev, unsigned int reg) case NVSW_REG_TACHO10_OFFSET: case NVSW_REG_TACHO11_OFFSET: case NVSW_REG_TACHO12_OFFSET: + case NVSW_REG_TACHO13_OFFSET: + case NVSW_REG_TACHO14_OFFSET: + case NVSW_REG_TACHO15_OFFSET: + case NVSW_REG_TACHO16_OFFSET: + case NVSW_REG_TACHO17_OFFSET: + case NVSW_REG_TACHO18_OFFSET: + case NVSW_REG_TACHO19_OFFSET: + case NVSW_REG_TACHO20_OFFSET: case NVSW_REG_FAN_CAP1_OFFSET: case NVSW_REG_FAN_DRW_CAP_OFFSET: case NVSW_REG_TACHO_SPEED_OFFSET: diff --git a/drivers/platform/mellanox/nvsw-host-spc6.c b/drivers/platform/mellanox/nvsw-host-spc6.c index ac3d42d4c70f..fb07af4d898a 100644 --- a/drivers/platform/mellanox/nvsw-host-spc6.c +++ b/drivers/platform/mellanox/nvsw-host-spc6.c @@ -29,10 +29,12 @@ * struct nvsw_host_spc6_cfg - per SKU board description * @regs_io: register access platform data; * @led: led platform data; + * @fan: fan platform data, NULL on systems without fans; */ struct nvsw_host_spc6_cfg { struct mlxreg_core_platform_data *regs_io; struct mlxreg_core_platform_data *led; + struct mlxreg_core_platform_data *fan; }; /* Platform mux channels */ @@ -65,6 +67,7 @@ static struct mlxcpld_mux_plat_data *nvsw_host_mux_data[NVSW_MUX_MAX]; static struct i2c_board_info *nvsw_host_mux_brdinfo; static struct mlxreg_core_platform_data *nvsw_led_data; static struct mlxreg_core_platform_data *nvsw_regs_io_data; +static struct mlxreg_core_platform_data *nvsw_fan_data; static struct mlxreg_core_platform_data *nvsw_wd_data[NVSW_WD_MAX]; static int nvsw_host_mux_num; static struct platform_device *nvsw_host_dev; @@ -565,6 +568,365 @@ static struct mlxreg_core_platform_data nvsw_host_spc6_lc_regs_io = { .counter = ARRAY_SIZE(nvsw_host_spc6_lc_regs_io_data), }; +/* Platform register access data for air cooled system */ +static struct mlxreg_core_data nvsw_host_spc6_ac_regs_io_data[] = { + { + .label = "cpld1_version", + .reg = NVSW_REG_CPLD1_VER_OFFSET, + .bit = GENMASK(7, 0), + .mode = 0444, + }, + { + .label = "cpld1_version_min", + .reg = NVSW_REG_CPLD1_MVER_OFFSET, + .bit = GENMASK(7, 0), + .mode = 0444, + }, + { + .label = "cpld2_version", + .reg = NVSW_REG_CPLD2_VER_OFFSET, + .bit = GENMASK(7, 0), + .mode = 0444, + }, + { + .label = "cpld2_version_min", + .reg = NVSW_REG_CPLD2_MVER_OFFSET, + .bit = GENMASK(7, 0), + .mode = 0444, + }, + { + .label = "cpld3_version", + .reg = NVSW_REG_CPLD3_VER_OFFSET, + .bit = GENMASK(7, 0), + .mode = 0444, + }, + { + .label = "cpld3_version_min", + .reg = NVSW_REG_CPLD3_MVER_OFFSET, + .bit = GENMASK(7, 0), + .mode = 0444, + }, + { + .label = "cpld4_version", + .reg = NVSW_REG_CPLD4_VER_OFFSET, + .bit = GENMASK(7, 0), + .mode = 0444, + }, + { + .label = "cpld4_version_min", + .reg = NVSW_REG_CPLD4_MVER_OFFSET, + .bit = GENMASK(7, 0), + .mode = 0444, + }, + { + .label = "cpld1_pn", + .reg = NVSW_REG_CPLD1_PN_OFFSET, + .bit = GENMASK(15, 0), + .mode = 0444, + .regnum = 2, + }, + { + .label = "cpld2_pn", + .reg = NVSW_REG_CPLD2_PN_OFFSET, + .bit = GENMASK(15, 0), + .mode = 0444, + .regnum = 2, + }, + { + .label = "cpld3_pn", + .reg = NVSW_REG_CPLD3_PN_OFFSET, + .bit = GENMASK(15, 0), + .mode = 0444, + .regnum = 2, + }, + { + .label = "cpld4_pn", + .reg = NVSW_REG_CPLD4_PN_OFFSET, + .bit = GENMASK(15, 0), + .mode = 0444, + .regnum = 2, + }, + { + .label = "asic_reset", + .reg = NVSW_REG_RESET_GP2_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(3), + .mode = 0644, + }, + { + .label = "reset_long_pb", + .reg = NVSW_REG_RESET_CAUSE_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(0), + .mode = 0444, + }, + { + .label = "reset_short_pb", + .reg = NVSW_REG_RESET_CAUSE_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(1), + .mode = 0444, + }, + { + .label = "reset_aux_pwr_or_reload", + .reg = NVSW_REG_RESET_CAUSE_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(2), + .mode = 0444, + }, + { + .label = "reset_swb_dc_dc_pwr_fail", + .reg = NVSW_REG_RESET_CAUSE_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(3), + .mode = 0444, + }, + { + .label = "reset_platform", + .reg = NVSW_REG_RESET_CAUSE_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(4), + .mode = 0444, + }, + { + .label = "reset_swb_wd", + .reg = NVSW_REG_RESET_CAUSE_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(6), + .mode = 0444, + }, + { + .label = "reset_asic_thermal", + .reg = NVSW_REG_RESET_CAUSE_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(7), + .mode = 0444, + }, + { + .label = "reset_soc", + .reg = NVSW_REG_RESET_CAUSE1_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(5), + .mode = 0444, + }, + { + .label = "reset_system", + .reg = NVSW_REG_RESET_CAUSE2_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(1), + .mode = 0444, + }, + { + .label = "reset_sw_pwr_off", + .reg = NVSW_REG_RESET_CAUSE2_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(2), + .mode = 0444, + }, + { + .label = "reset_cpu_thermal", + .reg = NVSW_REG_RESET_CAUSE2_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(3), + .mode = 0444, + }, + { + .label = "reset_ac_pwr_fail", + .reg = NVSW_REG_RESET_CAUSE2_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(6), + .mode = 0444, + }, + { + .label = "reset_mgmt_pwr", + .reg = NVSW_REG_RESET_CAUSE2_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(7), + .mode = 0444, + }, + { + .label = "fan_dir", + .reg = NVSW_REG_GP0_RO_OFFSET, + .bit = GENMASK(7, 0), + .mode = 0444, + }, + { + .label = "cpu_mctp_ready", + .reg = NVSW_REG_GP0_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(1), + .mode = 0644, + }, + { + .label = "cpu_shutdown_req", + .reg = NVSW_REG_GP0_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(2), + .mode = 0444, + }, + { + .label = "vpd_wp", + .reg = NVSW_REG_GP0_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(3), + .mode = 0644, + .secured = 1, + }, + { + .label = "pcie_asic_reset_dis", + .reg = NVSW_REG_GP0_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(4), + .mode = 0644, + }, + { + .label = "cpu_power_off_ready", + .reg = NVSW_REG_GP0_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(6), + .mode = 0644, + }, + { + .label = "pwr_converter_prog_en", + .reg = NVSW_REG_GP7_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(0), + .mode = 0644, + }, + { + .label = "graceful_pwr_off", + .reg = NVSW_REG_GP7_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(3), + .mode = 0644, + }, + { + .label = "pwr_cycle", + .reg = NVSW_REG_GP1_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(2), + .mode = 0244, + }, + { + .label = "pwr_down", + .reg = NVSW_REG_GP1_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(3), + .mode = 0200, + }, + { + .label = "aux_pwr_cycle", + .reg = NVSW_REG_GP1_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(4), + .mode = 0200, + }, + { + .label = "bmc_to_cpu_ctrl", + .reg = NVSW_REG_GP1_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(5), + .mode = 0644, + }, + { + .label = "uart_sel", + .reg = NVSW_REG_GP1_OFFSET, + .mask = NVSW_UART_SEL_MASK, + .bit = 7, + .mode = 0644, + }, + { + .label = "psu1_on", + .reg = NVSW_REG_GP4_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(0), + .mode = 0200, + }, + { + .label = "psu2_on", + .reg = NVSW_REG_GP4_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(1), + .mode = 0200, + }, + { + .label = "psu3_on", + .reg = NVSW_REG_GP4_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(2), + .mode = 0200, + }, + { + .label = "psu4_on", + .reg = NVSW_REG_GP4_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(3), + .mode = 0200, + }, + { + .label = "jtag_enable", + .reg = NVSW_REG_FIELD_UPGRADE, + .mask = GENMASK(1, 0), + .bit = 1, + .mode = 0644, + }, + { + .label = "jtag_cap", + .reg = NVSW_REG_FU_CAP_OFFSET, + .mask = NVSW_FU_CAP_MASK, + .bit = 1, + .mode = 0444, + }, + { + .label = "asic_health", + .reg = NVSW_REG_ASIC1_HEALTH_OFFSET, + .mask = NVSW_ASIC_MASK, + .bit = 1, + .mode = 0444, + }, + { + .label = "psu_status", + .reg = NVSW_REG_PS_2_OFFSET, + .bit = GENMASK(7, 0), + .mode = 0444, + }, + { + .label = "psu_event", + .reg = NVSW_REG_PS_2_EVENT_OFFSET, + .mask = GENMASK(7, 0), + .bit = 1, + .mode = 0644, + }, + { + .label = "psu_ac_status", + .reg = NVSW_REG_PS_AC_2_OFFSET, + .bit = GENMASK(7, 0), + .mode = 0444, + }, + { + .label = "psu_ac_event", + .reg = NVSW_REG_PS_AC_2_EVENT_OFFSET, + .mask = GENMASK(7, 0), + .bit = 1, + .mode = 0644, + }, + { + .label = "psu_dc_status", + .reg = NVSW_REG_PS_DC_OK_OFFSET, + .bit = GENMASK(7, 0), + .mode = 0444, + }, + { + .label = "psu_dc_event", + .reg = NVSW_REG_PS_DC_OK_EVENT_OFFSET, + .mask = GENMASK(7, 0), + .bit = 1, + .mode = 0644, + }, + { + .label = "fan_status", + .reg = NVSW_REG_FAN_OFFSET, + .bit = GENMASK(7, 0), + .mode = 0444, + }, + { + .label = "fan_event", + .reg = NVSW_REG_FAN_EVENT_OFFSET, + .mask = GENMASK(7, 0), + .bit = 1, + .mode = 0644, + }, + { + .label = "asic_pg_fail", + .reg = NVSW_REG_GP4_RO_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(7), + .mode = 0444, + }, + { + .label = "spi_chnl_select", + .reg = NVSW_REG_SPI_CHNL_SELECT, + .mask = GENMASK(7, 0), + .bit = 1, + .mode = 0644, + }, +}; + +static struct mlxreg_core_platform_data nvsw_host_spc6_ac_regs_io = { + .data = nvsw_host_spc6_ac_regs_io_data, + .counter = ARRAY_SIZE(nvsw_host_spc6_ac_regs_io_data), +}; + /* Platform led data for liquid cooled system */ static struct mlxreg_core_data nvsw_host_spc6_lc_led_data[] = { { @@ -599,6 +961,219 @@ static struct mlxreg_core_platform_data nvsw_host_spc6_lc_led = { .counter = ARRAY_SIZE(nvsw_host_spc6_lc_led_data), }; +/* Platform led data for air cooled system */ +static struct mlxreg_core_data nvsw_host_spc6_ac_led_data[] = { + { + .label = "status:green", + .reg = NVSW_REG_LED1_OFFSET, + .mask = NVSW_LED_LO_NIBBLE_MASK, + }, + { + .label = "status:amber", + .reg = NVSW_REG_LED1_OFFSET, + .mask = NVSW_LED_LO_NIBBLE_MASK, + }, + { + .label = "psu:green", + .reg = NVSW_REG_LED1_OFFSET, + .mask = NVSW_LED_HI_NIBBLE_MASK, + }, + { + .label = "psu:amber", + .reg = NVSW_REG_LED1_OFFSET, + .mask = NVSW_LED_HI_NIBBLE_MASK, + }, + { + .label = "fan:green", + .reg = NVSW_REG_LED6_OFFSET, + .mask = NVSW_LED_LO_NIBBLE_MASK, + }, + { + .label = "fan:amber", + .reg = NVSW_REG_LED6_OFFSET, + .mask = NVSW_LED_LO_NIBBLE_MASK, + }, + { + .label = "uid:blue", + .reg = NVSW_REG_LED5_OFFSET, + .mask = NVSW_LED_LO_NIBBLE_MASK, + }, + { + .label = "fan1:green", + .reg = NVSW_REG_LED2_OFFSET, + .mask = NVSW_LED_LO_NIBBLE_MASK, + .capability = NVSW_REG_FAN_DRW_CAP_OFFSET, + .slot = 1, + }, + { + .label = "fan1:orange", + .reg = NVSW_REG_LED2_OFFSET, + .mask = NVSW_LED_LO_NIBBLE_MASK, + .capability = NVSW_REG_FAN_DRW_CAP_OFFSET, + .slot = 1, + }, + { + .label = "fan2:green", + .reg = NVSW_REG_LED2_OFFSET, + .mask = NVSW_LED_HI_NIBBLE_MASK, + .capability = NVSW_REG_FAN_DRW_CAP_OFFSET, + .slot = 2, + }, + { + .label = "fan2:orange", + .reg = NVSW_REG_LED2_OFFSET, + .mask = NVSW_LED_HI_NIBBLE_MASK, + .capability = NVSW_REG_FAN_DRW_CAP_OFFSET, + .slot = 2, + }, + { + .label = "fan3:green", + .reg = NVSW_REG_LED3_OFFSET, + .mask = NVSW_LED_LO_NIBBLE_MASK, + .capability = NVSW_REG_FAN_DRW_CAP_OFFSET, + .slot = 3, + }, + { + .label = "fan3:orange", + .reg = NVSW_REG_LED3_OFFSET, + .mask = NVSW_LED_LO_NIBBLE_MASK, + .capability = NVSW_REG_FAN_DRW_CAP_OFFSET, + .slot = 3, + }, + { + .label = "fan4:green", + .reg = NVSW_REG_LED3_OFFSET, + .mask = NVSW_LED_HI_NIBBLE_MASK, + .capability = NVSW_REG_FAN_DRW_CAP_OFFSET, + .slot = 4, + }, + { + .label = "fan4:orange", + .reg = NVSW_REG_LED3_OFFSET, + .mask = NVSW_LED_HI_NIBBLE_MASK, + .capability = NVSW_REG_FAN_DRW_CAP_OFFSET, + .slot = 4, + }, + { + .label = "fan5:green", + .reg = NVSW_REG_LED4_OFFSET, + .mask = NVSW_LED_LO_NIBBLE_MASK, + .capability = NVSW_REG_FAN_DRW_CAP_OFFSET, + .slot = 5, + }, + { + .label = "fan5:orange", + .reg = NVSW_REG_LED4_OFFSET, + .mask = NVSW_LED_LO_NIBBLE_MASK, + .capability = NVSW_REG_FAN_DRW_CAP_OFFSET, + .slot = 5, + }, +}; + +static struct mlxreg_core_platform_data nvsw_host_spc6_ac_led = { + .data = nvsw_host_spc6_ac_led_data, + .counter = ARRAY_SIZE(nvsw_host_spc6_ac_led_data), +}; + +/* Platform fan data for air cooled system */ +static struct mlxreg_core_data nvsw_host_spc6_ac_fan_data[] = { + { + .label = "pwm1", + .reg = NVSW_REG_PWM1_OFFSET, + }, + { + .label = "tacho1", + .reg = NVSW_REG_TACHO1_OFFSET, + .mask = GENMASK(7, 0), + .capability = NVSW_REG_FAN_CAP1_OFFSET, + .slot = 1, + .reg_prsnt = NVSW_REG_FAN_OFFSET, + }, + { + .label = "tacho2", + .reg = NVSW_REG_TACHO2_OFFSET, + .mask = GENMASK(7, 0), + .capability = NVSW_REG_FAN_CAP1_OFFSET, + .slot = 2, + .reg_prsnt = NVSW_REG_FAN_OFFSET, + }, + { + .label = "tacho3", + .reg = NVSW_REG_TACHO3_OFFSET, + .mask = GENMASK(7, 0), + .capability = NVSW_REG_FAN_CAP1_OFFSET, + .slot = 3, + .reg_prsnt = NVSW_REG_FAN_OFFSET, + }, + { + .label = "tacho4", + .reg = NVSW_REG_TACHO4_OFFSET, + .mask = GENMASK(7, 0), + .capability = NVSW_REG_FAN_CAP1_OFFSET, + .slot = 4, + .reg_prsnt = NVSW_REG_FAN_OFFSET, + }, + { + .label = "tacho5", + .reg = NVSW_REG_TACHO5_OFFSET, + .mask = GENMASK(7, 0), + .capability = NVSW_REG_FAN_CAP1_OFFSET, + .slot = 5, + .reg_prsnt = NVSW_REG_FAN_OFFSET, + }, + { + .label = "tacho6", + .reg = NVSW_REG_TACHO6_OFFSET, + .mask = GENMASK(7, 0), + .capability = NVSW_REG_FAN_CAP1_OFFSET, + .slot = 6, + .reg_prsnt = NVSW_REG_FAN_OFFSET, + }, + { + .label = "tacho7", + .reg = NVSW_REG_TACHO7_OFFSET, + .mask = GENMASK(7, 0), + .capability = NVSW_REG_FAN_CAP1_OFFSET, + .slot = 7, + .reg_prsnt = NVSW_REG_FAN_OFFSET, + }, + { + .label = "tacho8", + .reg = NVSW_REG_TACHO8_OFFSET, + .mask = GENMASK(7, 0), + .capability = NVSW_REG_FAN_CAP1_OFFSET, + .slot = 8, + .reg_prsnt = NVSW_REG_FAN_OFFSET, + }, + { + .label = "tacho9", + .reg = NVSW_REG_TACHO9_OFFSET, + .mask = GENMASK(7, 0), + .capability = NVSW_REG_FAN_CAP1_OFFSET, + .slot = 9, + .reg_prsnt = NVSW_REG_FAN_OFFSET, + }, + { + .label = "tacho10", + .reg = NVSW_REG_TACHO10_OFFSET, + .mask = GENMASK(7, 0), + .capability = NVSW_REG_FAN_CAP1_OFFSET, + .slot = 10, + .reg_prsnt = NVSW_REG_FAN_OFFSET, + }, + { + .label = "conf", + .capability = NVSW_REG_TACHO_SPEED_OFFSET, + }, +}; + +static struct mlxreg_core_platform_data nvsw_host_spc6_ac_fan = { + .data = nvsw_host_spc6_ac_fan_data, + .counter = ARRAY_SIZE(nvsw_host_spc6_ac_fan_data), + .capability = NVSW_REG_FAN_DRW_CAP_OFFSET, + .version = 1, +}; + /* Watchdog platform data */ static struct mlxreg_core_data nvsw_host_spc6_wd_main_regs[] = { { @@ -722,6 +1297,13 @@ static const struct nvsw_host_spc6_cfg nvsw_host_spc6_cfg_lc = { .led = &nvsw_host_spc6_lc_led, }; +/* Air cooled system, equipped with power supplies and fan drawers. */ +static const struct nvsw_host_spc6_cfg nvsw_host_spc6_cfg_ac = { + .regs_io = &nvsw_host_spc6_ac_regs_io, + .led = &nvsw_host_spc6_ac_led, + .fan = &nvsw_host_spc6_ac_fan, +}; + static int __init nvsw_host_dmi_spc6_switch_matched(const struct dmi_system_id *dmi) { const struct nvsw_host_spc6_cfg *cfg = dmi->driver_data; @@ -734,6 +1316,7 @@ static int __init nvsw_host_dmi_spc6_switch_matched(const struct dmi_system_id * nvsw_host_mux_brdinfo = &nvsw_host_spc6_mux_brdinfo; nvsw_regs_io_data = cfg->regs_io; nvsw_led_data = cfg->led; + nvsw_fan_data = cfg->fan; for (i = 0; i < ARRAY_SIZE(nvsw_host_spc6_wd_set); i++) nvsw_wd_data[i] = &nvsw_host_spc6_wd_set[i]; @@ -741,6 +1324,14 @@ static int __init nvsw_host_dmi_spc6_switch_matched(const struct dmi_system_id * } static const struct dmi_system_id nvsw_host_dmi_table[] __initconst = { + { + .callback = nvsw_host_dmi_spc6_switch_matched, + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "VMOD0025"), + DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "HI186"), + }, + .driver_data = (void *)&nvsw_host_spc6_cfg_ac, + }, { .callback = nvsw_host_dmi_spc6_switch_matched, .matches = { @@ -833,6 +1424,7 @@ static int nvsw_host_probe(struct platform_device *pdev) nvsw_core->wd_data[i] = nvsw_wd_data[i]; nvsw_core->regio_data = nvsw_regs_io_data; nvsw_core->led_data = nvsw_led_data; + nvsw_core->fan_data = nvsw_fan_data; nvsw_core->mux_init = nvsw_host_spc6_mux_topology_init; nvsw_core->mux_exit = nvsw_host_spc6_mux_topology_exit; platform_set_drvdata(pdev, nvsw_core); diff --git a/drivers/platform/mellanox/nvsw.h b/drivers/platform/mellanox/nvsw.h index 88b16d0a36d4..56fac0582b2a 100644 --- a/drivers/platform/mellanox/nvsw.h +++ b/drivers/platform/mellanox/nvsw.h @@ -48,6 +48,9 @@ struct regmap; #define NVSW_REG_RESET_CAUSE1_OFFSET 0x251e #define NVSW_REG_RESET_CAUSE2_OFFSET 0x251f #define NVSW_REG_LED1_OFFSET 0x2520 +#define NVSW_REG_LED2_OFFSET 0x2521 +#define NVSW_REG_LED3_OFFSET 0x2522 +#define NVSW_REG_LED4_OFFSET 0x2523 #define NVSW_REG_LED5_OFFSET 0x2524 #define NVSW_REG_LED6_OFFSET 0x2525 #define NVSW_REG_LED7_OFFSET 0x2526 @@ -96,12 +99,21 @@ struct regmap; #define NVSW_REG_VR1_ALERT_OFFSET 0x2558 #define NVSW_REG_VR1_ALERT_EVENT_OFFSET 0x2559 #define NVSW_REG_VR1_ALERT_MASK_OFFSET 0x255a +#define NVSW_REG_PS_2_OFFSET 0x255b +#define NVSW_REG_PS_2_EVENT_OFFSET 0x255c +#define NVSW_REG_PS_2_MASK_OFFSET 0x255d #define NVSW_REG_VR2_ALERT_OFFSET 0x255e #define NVSW_REG_VR2_ALERT_EVENT_OFFSET 0x255f #define NVSW_REG_VR2_ALERT_MASK_OFFSET 0x2560 +#define NVSW_REG_PS_AC_2_OFFSET 0x2561 +#define NVSW_REG_PS_AC_2_EVENT_OFFSET 0x2562 +#define NVSW_REG_PS_AC_2_MASK_OFFSET 0x2563 #define NVSW_REG_PS_DC_OK_OFFSET 0x2567 #define NVSW_REG_PS_DC_OK_EVENT_OFFSET 0x2568 #define NVSW_REG_PS_DC_OK_MASK_OFFSET 0x2569 +#define NVSW_REG_PS_ALARM_OFFSET 0x256d +#define NVSW_REG_PS_ALARM_EVENT_OFFSET 0x256e +#define NVSW_REG_PS_ALARM_MASK_OFFSET 0x256f #define NVSW_REG_CPLD8_PN_OFFSET 0x2573 #define NVSW_REG_CPLD8_PN1_OFFSET 0x2574 #define NVSW_REG_PG3_OFFSET 0x2576 @@ -142,6 +154,10 @@ struct regmap; #define NVSW_REG_LEAK_OFFSET 0x25af #define NVSW_REG_LEAK_EVENT_OFFSET 0x25b0 #define NVSW_REG_LEAK_MASK_OFFSET 0x25b1 +#define NVSW_REG_TACHO19_OFFSET 0x25b4 +#define NVSW_REG_TACHO20_OFFSET 0x25b5 +#define NVSW_REG_TACHO17_OFFSET 0x25ba +#define NVSW_REG_TACHO18_OFFSET 0x25bb #define NVSW_REG_CURRENT_TEMP_RO 0x25c0 #define NVSW_REG_SWITCH_IC_QTY 0x25c1 #define NVSW_REG_GP4_RO_OFFSET 0x25c2 @@ -184,6 +200,8 @@ struct regmap; #define NVSW_REG_TACHO10_OFFSET 0x25ee #define NVSW_REG_TACHO11_OFFSET 0x25ef #define NVSW_REG_TACHO12_OFFSET 0x25f0 +#define NVSW_REG_TACHO13_OFFSET 0x25f1 +#define NVSW_REG_TACHO14_OFFSET 0x25f2 #define NVSW_REG_FAN_CAP1_OFFSET 0x25f5 #define NVSW_REG_FAN_DRW_CAP_OFFSET 0x25f7 #define NVSW_REG_TACHO_SPEED_OFFSET 0x25f8 @@ -191,6 +209,8 @@ struct regmap; #define NVSW_REG_CONFIG1_OFFSET 0x25fb #define NVSW_REG_CONFIG2_OFFSET 0x25fc #define NVSW_REG_CONFIG3_OFFSET 0x25fd +#define NVSW_REG_TACHO15_OFFSET 0x25fe +#define NVSW_REG_TACHO16_OFFSET 0x25ff #define NVSW_REG_MIN 0x2500 #define NVSW_REG_MAX 0x26ff -- 2.34.1 ^ permalink raw reply related [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-10-16 11:25 UTC | newest] Thread overview: 5+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-10-08 6:17 [PATCH platform-next 0/3] platform/mellanox: Add nvsw core and SPC6 host driver Felix Radensky 2026-10-08 6:17 ` [PATCH platform-next 1/3] platform/mellanox: nvsw-core: Add core driver for Nvidia switches Felix Radensky 2026-10-08 6:17 ` [PATCH platform-next 2/3] platform/mellanox: nvsw-host-spc6: Add driver for Nvidia SPC6 switches Felix Radensky 2026-10-16 11:25 ` Ilpo Järvinen 2026-10-08 6:17 ` [PATCH platform-next 3/3] platform/mellanox: nvsw-host-spc6: Add support for SPC6 air cooled switches Felix Radensky
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.