Hi Ilpo! > -----Original Message----- > From: Ilpo Järvinen <ilpo.jarvinen@xxxxxxxxxxxxxxx> > Sent: Friday, 17 January 2025 18:59 > To: Vadim Pasternak <vadimp@xxxxxxxxxx> > Cc: Hans de Goede <hdegoede@xxxxxxxxxx>; Michael Shych > <michaelsh@xxxxxxxxxx>; Ciju Rajan K <crajank@xxxxxxxxxx>; Felix Radensky > <fradensky@xxxxxxxxxx>; Oleksandr Shamray <oleksandrs@xxxxxxxxxx>; > platform-driver-x86@xxxxxxxxxxxxxxx > Subject: Re: [PATCH v3 06/10] platform/mellanox: mlxreg-dpu: Add initial > support for Nvidia DPU > > On Thu, 16 Jan 2025, Vadim Pasternak wrote: > > > Provide platform support for Nvidia (DPU) Data Processor Unit for the > > Smart Switch SN4280. > > > > The Smart Switch equipped with: > > - Nvidia COME module based on AMD EPYC™ Embedded 3451 CPU. > > - Nvidia Spectrum-3 ASIC. > > - Four DPUs, each equipped with Nvidia BF3 ARM based processor and > > with Lattice LFD2NX-40 FPGA device. > > - 28xQSFP-DD external ports. > > - Two power supplies. > > - Four cooling drawers. > > > > Drivers provides support for the platform management and monitoring of > > DPU components. > > It includes support for health events, resets and boot progress > > indications logic, implemented by FPGA device. > > > > Reviewed-by: Ciju Rajan K <crajank@xxxxxxxxxx> > > Signed-off-by: Vadim Pasternak <vadimp@xxxxxxxxxx> > > --- > > Comments pointed out by Ilpo: > > - Fix s/pltaform/platform. > > - Remove semicolon from structure description. > > - In routine mlxreg_dpu_copy_hotplug_data() use 'const struct' for the > > third argument. > > - In mlxreg_dpu_copy_hotplug_data() remove redunadant > devm_kmemdup() > > call. > > - Fix identifications in mlxreg_dpu_config_init(). > > - Remove label 'fail_register_io" from error flow. > > - One line for devm_regmap_init_i2c() call in mlxreg_dpu_probe(). > > --- > > drivers/platform/mellanox/Kconfig | 12 + > > drivers/platform/mellanox/Makefile | 1 + > > drivers/platform/mellanox/mlxreg-dpu.c | 615 > > +++++++++++++++++++++++++ > > 3 files changed, 628 insertions(+) > > create mode 100644 drivers/platform/mellanox/mlxreg-dpu.c > > > > diff --git a/drivers/platform/mellanox/Kconfig > > b/drivers/platform/mellanox/Kconfig > > index aa760f064a17..7da0fc46b1e7 100644 > > --- a/drivers/platform/mellanox/Kconfig > > +++ b/drivers/platform/mellanox/Kconfig > > @@ -27,6 +27,18 @@ config MLX_PLATFORM > > > > If you have a Mellanox system, say Y or M here. > > > > +config MLXREG_DPU > > + tristate "Nvidia Data Processor Unit platform driver support" > > + select REGMAP_I2C > > + help > > + This driver provides support for the Nvidia BF3 Data Processor Units, > > + which are the part of SN4280 Ethernet smart switch systems > > + providing a high performance switching solution for Enterprise Data > > + Centers (EDC) for building Ethernet based clusters, High-Performance > > + Computing (HPC) and embedded environments. > > + > > + If you have a Nvidia smart swicth system, say Y or M here. > > + > > config MLXREG_HOTPLUG > > tristate "Mellanox platform hotplug driver support" > > depends on HWMON > > diff --git a/drivers/platform/mellanox/Makefile > > b/drivers/platform/mellanox/Makefile > > index ba56485cbe8c..e86723b44c2e 100644 > > --- a/drivers/platform/mellanox/Makefile > > +++ b/drivers/platform/mellanox/Makefile > > @@ -7,6 +7,7 @@ obj-$(CONFIG_MLX_PLATFORM) += mlx-platform.o > > obj-$(CONFIG_MLXBF_BOOTCTL) += mlxbf-bootctl.o > > obj-$(CONFIG_MLXBF_PMC) += mlxbf-pmc.o > > obj-$(CONFIG_MLXBF_TMFIFO) += mlxbf-tmfifo.o > > +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 diff --git > > a/drivers/platform/mellanox/mlxreg-dpu.c > > b/drivers/platform/mellanox/mlxreg-dpu.c > > new file mode 100644 > > index 000000000000..0d4cede4eebe > > --- /dev/null > > +++ b/drivers/platform/mellanox/mlxreg-dpu.c > > @@ -0,0 +1,615 @@ > > +// SPDX-License-Identifier: GPL-2.0+ > > +/* > > + * Nvidia Data Processor Unit platform driver > > + * > > + * Copyright (C) 2025 Nvidia Technologies Ltd. > > + */ > > + > > +#include <linux/device.h> > > +#include <linux/i2c.h> > > +#include <linux/module.h> > > +#include <linux/platform_data/mlxcpld.h> #include > > +<linux/platform_data/mlxreg.h> #include <linux/platform_device.h> > > +#include <linux/regmap.h> > > + > > +/* I2C bus IO offsets */ > > +#define MLXREG_DPU_REG_FPGA1_VER_OFFSET > 0x2400 > > +#define MLXREG_DPU_REG_FPGA1_PN_OFFSET > 0x2404 > > +#define MLXREG_DPU_REG_FPGA1_PN1_OFFSET > 0x2405 > > +#define MLXREG_DPU_REG_PG_OFFSET 0x2414 > > +#define MLXREG_DPU_REG_PG_EVENT_OFFSET > 0x2415 > > +#define MLXREG_DPU_REG_PG_MASK_OFFSET > 0x2416 > > +#define MLXREG_DPU_REG_RESET_GP1_OFFSET > 0x2417 > > +#define MLXREG_DPU_REG_RST_CAUSE1_OFFSET 0x241e > > +#define MLXREG_DPU_REG_GP0_RO_OFFSET > 0x242b > > +#define MLXREG_DPU_REG_GP0_OFFSET 0x242e > > +#define MLXREG_DPU_REG_GP1_OFFSET 0x242c > > +#define MLXREG_DPU_REG_GP4_OFFSET 0x2438 > > +#define MLXREG_DPU_REG_AGGRCO_OFFSET > 0x2442 > > +#define MLXREG_DPU_REG_AGGRCO_MASK_OFFSET > 0x2443 > > +#define MLXREG_DPU_REG_HEALTH_OFFSET 0x244d > > +#define MLXREG_DPU_REG_HEALTH_EVENT_OFFSET 0x244e > > +#define MLXREG_DPU_REG_HEALTH_MASK_OFFSET 0x244f > > +#define MLXREG_DPU_REG_FPGA1_MVER_OFFSET 0x24de > > +#define MLXREG_DPU_REG_CONFIG3_OFFSET > 0x24fd > > +#define MLXREG_DPU_REG_MAX 0x3fff > > + > > +/* Power Good event masks. */ > > +#define MLXREG_DPU_PG_VDDIO_MASK BIT(0) > > +#define MLXREG_DPU_PG_VDD_CPU_MASK BIT(1) > > +#define MLXREG_DPU_PG_VDD_MASK BIT(2) > > +#define MLXREG_DPU_PG_1V8_MASK BIT(3) > > +#define MLXREG_DPU_PG_COMPARATOR_MASK BIT(4) > > +#define MLXREG_DPU_PG_VDDQ_MASK BIT(5) > > +#define MLXREG_DPU_PG_HVDD_MASK BIT(6) > > +#define MLXREG_DPU_PG_DVDD_MASK BIT(7) > > +#define MLXREG_DPU_PG_MASK > (MLXREG_DPU_PG_DVDD_MASK | \ > > + > MLXREG_DPU_PG_HVDD_MASK | \ > > + > MLXREG_DPU_PG_VDDQ_MASK | \ > > + > MLXREG_DPU_PG_COMPARATOR_MASK | \ > > + > MLXREG_DPU_PG_1V8_MASK | \ > > + > MLXREG_DPU_PG_VDD_CPU_MASK | \ > > + > MLXREG_DPU_PG_VDD_MASK | \ > > + > MLXREG_DPU_PG_VDDIO_MASK) > > + > > +/* Health event masks. */ > > +#define MLXREG_DPU_HLTH_THERMAL_TRIP_MASK BIT(0) > > +#define MLXREG_DPU_HLTH_UFM_UPGRADE_DONE_MASK BIT(1) > > +#define MLXREG_DPU_HLTH_VDDQ_HOT_ALERT_MASK BIT(2) > > +#define MLXREG_DPU_HLTH_VDD_CPU_HOT_ALERT_MASK BIT(3) > > +#define MLXREG_DPU_HLTH_VDDQ_ALERT_MASK BIT(4) > > +#define MLXREG_DPU_HLTH_VDD_CPU_ALERT_MASK BIT(5) > > +#define MLXREG_DPU_HEALTH_MASK > (MLXREG_DPU_HLTH_UFM_UPGRADE_DONE_MASK | \ > > + > MLXREG_DPU_HLTH_VDDQ_HOT_ALERT_MASK | \ > > + > MLXREG_DPU_HLTH_VDD_CPU_HOT_ALERT_MASK | \ > > + > MLXREG_DPU_HLTH_VDDQ_ALERT_MASK | \ > > + > MLXREG_DPU_HLTH_VDD_CPU_ALERT_MASK | \ > > + > MLXREG_DPU_HLTH_THERMAL_TRIP_MASK) > > + > > +/* Hotplug aggregation masks. */ > > +#define MLXREG_DPU_HEALTH_AGGR_MASK BIT(0) > > +#define MLXREG_DPU_PG_AGGR_MASK BIT(1) > > +#define MLXREG_DPU_AGGR_MASK > (MLXREG_DPU_HEALTH_AGGR_MASK | \ > > + > MLXREG_DPU_PG_AGGR_MASK) > > + > > +/* Voltage regulator firmware update status mask. */ > > +#define MLXREG_DPU_VOLTREG_UPD_MASK > GENMASK(5, 4) > > + > > +#define MLXREG_DPU_NR_NONE (-1) > > + > > +/* > > + * enum mlxreg_dpu_type - Data Processor Unit types > > + * > > + * @MLXREG_DPU_BF3: DPU equipped with BF3 SoC; */ enum > > +mlxreg_dpu_type { > > + MLXREG_DPU_BF3 = 0x0050, > > +}; > > + > > +/* Default register access data. */ > > +static struct mlxreg_core_data mlxreg_dpu_io_data[] = { > > + { > > + .label = "fpga1_version", > > + .reg = MLXREG_DPU_REG_FPGA1_VER_OFFSET, > > + .bit = GENMASK(7, 0), > > + .mode = 0444, > > + }, > > + { > > + .label = "fpga1_pn", > > + .reg = MLXREG_DPU_REG_FPGA1_PN_OFFSET, > > + .bit = GENMASK(15, 0), > > + .mode = 0444, > > + .regnum = 2, > > + }, > > + { > > + .label = "fpga1_version_min", > > + .reg = MLXREG_DPU_REG_FPGA1_MVER_OFFSET, > > + .bit = GENMASK(7, 0), > > + .mode = 0444, > > + }, > > + { > > + .label = "perst_rst", > > + .reg = MLXREG_DPU_REG_RESET_GP1_OFFSET, > > + .mask = GENMASK(7, 0) & ~BIT(0), > > + .mode = 0644, > > + }, > > + { > > + .label = "usbphy_rst", > > + .reg = MLXREG_DPU_REG_RESET_GP1_OFFSET, > > + .mask = GENMASK(7, 0) & ~BIT(1), > > + .mode = 0644, > > + }, > > + { > > + .label = "phy_rst", > > + .reg = MLXREG_DPU_REG_RESET_GP1_OFFSET, > > + .mask = GENMASK(7, 0) & ~BIT(2), > > + .mode = 0644, > > + }, > > + { > > + .label = "tpm_rst", > > + .reg = MLXREG_DPU_REG_RESET_GP1_OFFSET, > > + .mask = GENMASK(7, 0) & ~BIT(6), > > + .mode = 0644, > > + }, > > + { > > + .label = "reset_from_main_board", > > + .reg = MLXREG_DPU_REG_RST_CAUSE1_OFFSET, > > + .mask = GENMASK(7, 0) & ~BIT(1), > > + .mode = 0444, > > + }, > > + { > > + .label = "reset_aux_pwr_or_reload", > > + .reg = MLXREG_DPU_REG_RST_CAUSE1_OFFSET, > > + .mask = GENMASK(7, 0) & ~BIT(2), > > + .mode = 0444, > > + }, > > + { > > + .label = "reset_comex_pwr_fail", > > + .reg = MLXREG_DPU_REG_RST_CAUSE1_OFFSET, > > + .mask = GENMASK(7, 0) & ~BIT(3), > > + .mode = 0444, > > + }, > > + { > > + .label = "reset_dpu_thermal", > > + .reg = MLXREG_DPU_REG_RST_CAUSE1_OFFSET, > > + .mask = GENMASK(7, 0) & ~BIT(6), > > + .mode = 0444, > > + }, > > + { > > + .label = "reset_pwr_off", > > + .reg = MLXREG_DPU_REG_RST_CAUSE1_OFFSET, > > + .mask = GENMASK(7, 0) & ~BIT(7), > > + .mode = 0444, > > + }, > > + { > > + .label = "dpu_id", > > + .reg = MLXREG_DPU_REG_GP0_RO_OFFSET, > > + .mask = GENMASK(3, 0), > > + .mode = 0444, > > + }, > > + { > > + .label = "voltreg_update_status", > > + .reg = MLXREG_DPU_REG_GP0_RO_OFFSET, > > + .mask = MLXREG_DPU_VOLTREG_UPD_MASK, > > + .bit = 5, > > + .mode = 0444, > > + }, > > + { > > + .label = "boot_progress", > > + .reg = MLXREG_DPU_REG_GP0_OFFSET, > > + .mask = GENMASK(3, 0), > > + .mode = 0444, > > + }, > > + { > > + .label = "ufm_upgrade", > > + .reg = MLXREG_DPU_REG_GP4_OFFSET, > > + .mask = GENMASK(7, 0) & ~BIT(1), > > + .mode = 0644, > > + }, > > +}; > > + > > +static struct mlxreg_core_platform_data mlxreg_dpu_default_regs_io_data > = { > > + .data = mlxreg_dpu_io_data, > > + .counter = ARRAY_SIZE(mlxreg_dpu_io_data), }; > > + > > +/* Default hotplug data. */ > > +static struct mlxreg_core_data mlxreg_dpu_power_events_items_data[] = { > > + { > > + .label = "pg_vddio", > > + .reg = MLXREG_DPU_REG_PG_OFFSET, > > + .mask = MLXREG_DPU_PG_VDDIO_MASK, > > + .hpdev.nr = MLXREG_DPU_NR_NONE, > > + }, > > + { > > + .label = "pg_vdd_cpu", > > + .reg = MLXREG_DPU_REG_PG_OFFSET, > > + .mask = MLXREG_DPU_PG_VDD_CPU_MASK, > > + .hpdev.nr = MLXREG_DPU_NR_NONE, > > + }, > > + { > > + .label = "pg_vdd", > > + .reg = MLXREG_DPU_REG_PG_OFFSET, > > + .mask = MLXREG_DPU_PG_VDD_MASK, > > + .hpdev.nr = MLXREG_DPU_NR_NONE, > > + }, > > + { > > + .label = "pg_1v8", > > + .reg = MLXREG_DPU_REG_PG_OFFSET, > > + .mask = MLXREG_DPU_PG_1V8_MASK, > > + .hpdev.nr = MLXREG_DPU_NR_NONE, > > + }, > > + { > > + .label = "pg_comparator", > > + .reg = MLXREG_DPU_REG_PG_OFFSET, > > + .mask = MLXREG_DPU_PG_COMPARATOR_MASK, > > + .hpdev.nr = MLXREG_DPU_NR_NONE, > > + }, > > + { > > + .label = "pg_vddq", > > + .reg = MLXREG_DPU_REG_PG_OFFSET, > > + .mask = MLXREG_DPU_PG_VDDQ_MASK, > > + .hpdev.nr = MLXREG_DPU_NR_NONE, > > + }, > > + { > > + .label = "pg_hvdd", > > + .reg = MLXREG_DPU_REG_PG_OFFSET, > > + .mask = MLXREG_DPU_PG_HVDD_MASK, > > + .hpdev.nr = MLXREG_DPU_NR_NONE, > > + }, > > + { > > + .label = "pg_dvdd", > > + .reg = MLXREG_DPU_REG_PG_OFFSET, > > + .mask = MLXREG_DPU_PG_DVDD_MASK, > > + .hpdev.nr = MLXREG_DPU_NR_NONE, > > + }, > > +}; > > + > > +static struct mlxreg_core_data mlxreg_dpu_health_events_items_data[] = { > > + { > > + .label = "thermal_trip", > > + .reg = MLXREG_DPU_REG_HEALTH_OFFSET, > > + .mask = MLXREG_DPU_HLTH_THERMAL_TRIP_MASK, > > + .hpdev.nr = MLXREG_DPU_NR_NONE, > > + }, > > + { > > + .label = "ufm_upgrade_done", > > + .reg = MLXREG_DPU_REG_HEALTH_OFFSET, > > + .mask = MLXREG_DPU_HLTH_UFM_UPGRADE_DONE_MASK, > > + .hpdev.nr = MLXREG_DPU_NR_NONE, > > + }, > > + { > > + .label = "vddq_hot_alert", > > + .reg = MLXREG_DPU_REG_HEALTH_OFFSET, > > + .mask = MLXREG_DPU_HLTH_VDDQ_HOT_ALERT_MASK, > > + .hpdev.nr = MLXREG_DPU_NR_NONE, > > + }, > > + { > > + .label = "vdd_cpu_hot_alert", > > + .reg = MLXREG_DPU_REG_HEALTH_OFFSET, > > + .mask = MLXREG_DPU_HLTH_VDD_CPU_HOT_ALERT_MASK, > > + .hpdev.nr = MLXREG_DPU_NR_NONE, > > + }, > > + { > > + .label = "vddq_alert", > > + .reg = MLXREG_DPU_REG_HEALTH_OFFSET, > > + .mask = MLXREG_DPU_HLTH_VDDQ_ALERT_MASK, > > + .hpdev.nr = MLXREG_DPU_NR_NONE, > > + }, > > + { > > + .label = "vdd_cpu_alert", > > + .reg = MLXREG_DPU_REG_HEALTH_OFFSET, > > + .mask = MLXREG_DPU_HLTH_VDD_CPU_ALERT_MASK, > > + .hpdev.nr = MLXREG_DPU_NR_NONE, > > + }, > > +}; > > + > > +static struct mlxreg_core_item mlxreg_dpu_hotplug_items[] = { > > + { > > + .data = mlxreg_dpu_power_events_items_data, > > + .aggr_mask = MLXREG_DPU_PG_AGGR_MASK, > > + .reg = MLXREG_DPU_REG_PG_OFFSET, > > + .mask = MLXREG_DPU_PG_MASK, > > + .count = > ARRAY_SIZE(mlxreg_dpu_power_events_items_data), > > + .health = false, > > + .inversed = 1, > > + }, > > + { > > + .data = mlxreg_dpu_health_events_items_data, > > + .aggr_mask = MLXREG_DPU_HEALTH_AGGR_MASK, > > + .reg = MLXREG_DPU_REG_HEALTH_OFFSET, > > + .mask = MLXREG_DPU_HEALTH_MASK, > > + .count = > ARRAY_SIZE(mlxreg_dpu_health_events_items_data), > > + .health = false, > > + .inversed = 1, > > + }, > > +}; > > + > > +static > > +struct mlxreg_core_hotplug_platform_data > mlxreg_dpu_default_hotplug_data = { > > + .items = mlxreg_dpu_hotplug_items, > > + .counter = ARRAY_SIZE(mlxreg_dpu_hotplug_items), > > + .cell = MLXREG_DPU_REG_AGGRCO_OFFSET, > > + .mask = MLXREG_DPU_AGGR_MASK, > > +}; > > + > > +/* mlxreg_dpu - device private data > > + * @dev: platform device > > + * @data: platform core data > > + * @io_data: register access platform data > > + * @io_regs: register access device > > + * @hotplug_data: hotplug platform data > > + * @hotplug: hotplug device > > + */ > > +struct mlxreg_dpu { > > + struct device *dev; > > + struct mlxreg_core_data *data; > > + struct mlxreg_core_platform_data *io_data; > > + struct platform_device *io_regs; > > + struct mlxreg_core_hotplug_platform_data *hotplug_data; > > + struct platform_device *hotplug; > > +}; > > + > > +static bool mlxreg_dpu_writeable_reg(struct device *dev, unsigned int > > +reg) { > > + switch (reg) { > > + case MLXREG_DPU_REG_PG_EVENT_OFFSET: > > + case MLXREG_DPU_REG_PG_MASK_OFFSET: > > + case MLXREG_DPU_REG_RESET_GP1_OFFSET: > > + case MLXREG_DPU_REG_GP0_OFFSET: > > + case MLXREG_DPU_REG_GP1_OFFSET: > > + case MLXREG_DPU_REG_GP4_OFFSET: > > + case MLXREG_DPU_REG_AGGRCO_OFFSET: > > + case MLXREG_DPU_REG_AGGRCO_MASK_OFFSET: > > + case MLXREG_DPU_REG_HEALTH_EVENT_OFFSET: > > + case MLXREG_DPU_REG_HEALTH_MASK_OFFSET: > > + return true; > > + } > > + return false; > > +} > > + > > +static bool mlxreg_dpu_readable_reg(struct device *dev, unsigned int > > +reg) { > > + switch (reg) { > > + case MLXREG_DPU_REG_FPGA1_VER_OFFSET: > > + case MLXREG_DPU_REG_FPGA1_PN_OFFSET: > > + case MLXREG_DPU_REG_FPGA1_PN1_OFFSET: > > + case MLXREG_DPU_REG_PG_OFFSET: > > + case MLXREG_DPU_REG_PG_EVENT_OFFSET: > > + case MLXREG_DPU_REG_PG_MASK_OFFSET: > > + case MLXREG_DPU_REG_RESET_GP1_OFFSET: > > + case MLXREG_DPU_REG_RST_CAUSE1_OFFSET: > > + case MLXREG_DPU_REG_GP0_RO_OFFSET: > > + case MLXREG_DPU_REG_GP0_OFFSET: > > + case MLXREG_DPU_REG_GP1_OFFSET: > > + case MLXREG_DPU_REG_GP4_OFFSET: > > + case MLXREG_DPU_REG_AGGRCO_OFFSET: > > + case MLXREG_DPU_REG_AGGRCO_MASK_OFFSET: > > + case MLXREG_DPU_REG_HEALTH_OFFSET: > > + case MLXREG_DPU_REG_HEALTH_EVENT_OFFSET: > > + case MLXREG_DPU_REG_HEALTH_MASK_OFFSET: > > + case MLXREG_DPU_REG_FPGA1_MVER_OFFSET: > > + case MLXREG_DPU_REG_CONFIG3_OFFSET: > > + return true; > > + } > > + return false; > > +} > > + > > +static bool mlxreg_dpu_volatile_reg(struct device *dev, unsigned int > > +reg) { > > + switch (reg) { > > + case MLXREG_DPU_REG_FPGA1_VER_OFFSET: > > + case MLXREG_DPU_REG_FPGA1_PN_OFFSET: > > + case MLXREG_DPU_REG_FPGA1_PN1_OFFSET: > > + case MLXREG_DPU_REG_PG_OFFSET: > > + case MLXREG_DPU_REG_PG_EVENT_OFFSET: > > + case MLXREG_DPU_REG_PG_MASK_OFFSET: > > + case MLXREG_DPU_REG_RESET_GP1_OFFSET: > > + case MLXREG_DPU_REG_RST_CAUSE1_OFFSET: > > + case MLXREG_DPU_REG_GP0_RO_OFFSET: > > + case MLXREG_DPU_REG_GP0_OFFSET: > > + case MLXREG_DPU_REG_GP1_OFFSET: > > + case MLXREG_DPU_REG_GP4_OFFSET: > > + case MLXREG_DPU_REG_AGGRCO_OFFSET: > > + case MLXREG_DPU_REG_AGGRCO_MASK_OFFSET: > > + case MLXREG_DPU_REG_HEALTH_OFFSET: > > + case MLXREG_DPU_REG_HEALTH_EVENT_OFFSET: > > + case MLXREG_DPU_REG_HEALTH_MASK_OFFSET: > > + case MLXREG_DPU_REG_FPGA1_MVER_OFFSET: > > + case MLXREG_DPU_REG_CONFIG3_OFFSET: > > + return true; > > + } > > + return false; > > +} > > + > > +/* Configuration for the register map of a device with 2 bytes > > +address space. */ static const struct regmap_config > mlxreg_dpu_regmap_conf = { > > + .reg_bits = 16, > > + .val_bits = 8, > > + .max_register = MLXREG_DPU_REG_MAX, > > + .cache_type = REGCACHE_FLAT, > > + .writeable_reg = mlxreg_dpu_writeable_reg, > > + .readable_reg = mlxreg_dpu_readable_reg, > > + .volatile_reg = mlxreg_dpu_volatile_reg, }; > > + > > +static int > > +mlxreg_dpu_copy_hotplug_data(struct device *dev, struct mlxreg_dpu > *mlxreg_dpu, > > + const struct mlxreg_core_hotplug_platform_data > *hotplug_data) > > +{ > > + struct mlxreg_core_item *item; > > + int i; > > + > > + mlxreg_dpu->hotplug_data = devm_kmemdup(dev, hotplug_data, > > + sizeof(*mlxreg_dpu- > >hotplug_data), GFP_KERNEL); > > + if (!mlxreg_dpu->hotplug_data) > > + return -ENOMEM; > > + > > + item = mlxreg_dpu->hotplug_data->items; > > + for (i = 0; i < mlxreg_dpu->hotplug_data->counter; i++, item++) { > > This still has the same issue wrt. what is assigned to item. The item related > code on the two lines above this point do nothing because the variable's value > will be overwritten by this: > > > + item = devm_kmemdup(dev, &hotplug_data->items[i], > sizeof(*item), > > +GFP_KERNEL); > > What is the intent here? The allocated item will be left dangling, only thing that > holds pointer to it after this iteration complets is the devm machinery, is that > the intention here? Or did you perhaps want to put it into the ->items (which > btw is no longer allocated at all since you didn't replace the memdup with > kcalloc)? > > If you don't understand what I'm trying to tell here, please try to explain what > this loop does, in terms of where you want the allocated item be stored into? Then intension was to copy 'mlxreg_dpu_default_hotplug_data' to 'mlxreg_dpu->hotplug_data' and then copy to this structure all underling fields: mlxreg_dpu_default_hotplug_data.items[i] and for each item 'mlxreg_dpu_default_hotplug_data.items[i.data[]'. I can do it with the below code: mlxreg_dpu->hotplug_data = devm_kmemdup(dev, hotplug_data, sizeof(*mlxreg_dpu->hotplug_data), GFP_KERNEL); if (!mlxreg_dpu->hotplug_data) return -ENOMEM; mlxreg_dpu->hotplug_data->items = kcalloc(hotplug_data->counter, sizeof(*item), GFP_KERNEL); if (!mlxreg_dpu->hotplug_data->items) return -ENOMEM; item = mlxreg_dpu->hotplug_data->items; for (i = 0; i < hotplug_data->counter; i++, item++) { memcpy(item, &hotplug_data->items[i], sizeof(*item)); item->data = kcalloc(hotplug_data->items[i].count, sizeof(*item->data), GFP_KERNEL); if (!item->data) goto kcalloc_fail; data = item->data; for (j = 0; j < hotplug_data->items[i].count; j++, data++) memcpy(data, &hotplug_data->items[i].data[j], sizeof(*data)); } Does it look OK? > > > + if (!item) > > + return -ENOMEM; > > + item->data = devm_kmemdup(dev, hotplug_data- > >items[i].data, > > + hotplug_data->items[i].count * > sizeof(item->data), > > + GFP_KERNEL); > > + if (!item->data) > > + return -ENOMEM; > > + } > > + > > + return 0; > > +} > > -- > i. > > > > + > > +static int mlxreg_dpu_config_init(struct mlxreg_dpu *mlxreg_dpu, void > *regmap, > > + struct mlxreg_core_data *data, int irq) > > +{ > > + struct device *dev = &data->hpdev.client->dev; > > + u32 regval; > > + int err; > > + > > + /* Validate DPU type. */ > > + err = regmap_read(regmap, MLXREG_DPU_REG_CONFIG3_OFFSET, > ®val); > > + if (err) > > + return err; > > + switch (regval) { > > + case MLXREG_DPU_BF3: > > + /* Copy platform specific hotplug data. */ > > + err = mlxreg_dpu_copy_hotplug_data(dev, mlxreg_dpu, > > + > &mlxreg_dpu_default_hotplug_data); > > + if (err) > > + return err; > > + > > + mlxreg_dpu->io_data = &mlxreg_dpu_default_regs_io_data; > > + > > + break; > > + default: > > + return -ENODEV; > > + } > > + > > + /* Register IO access driver. */ > > + if (mlxreg_dpu->io_data) { > > + mlxreg_dpu->io_data->regmap = regmap; > > + mlxreg_dpu->io_regs = > > + platform_device_register_resndata(dev, "mlxreg-io", > > + data->slot, NULL, 0, > > + mlxreg_dpu- > >io_data, > > + sizeof(*mlxreg_dpu- > >io_data)); > > + if (IS_ERR(mlxreg_dpu->io_regs)) { > > + dev_err(dev, "Failed to create regio for client %s at bus > %d at addr 0x%02x\n", > > + data->hpdev.brdinfo->type, data->hpdev.nr, > > + data->hpdev.brdinfo->addr); > > + return PTR_ERR(mlxreg_dpu->io_regs); > > + } > > + } > > + > > + /* Register hotplug driver. */ > > + if (mlxreg_dpu->hotplug_data && irq) { > > + mlxreg_dpu->hotplug_data->regmap = regmap; > > + mlxreg_dpu->hotplug_data->irq = irq; > > + mlxreg_dpu->hotplug = > > + platform_device_register_resndata(dev, "mlxreg- > hotplug", > > + data->slot, NULL, 0, > > + mlxreg_dpu- > >hotplug_data, > > + sizeof(*mlxreg_dpu- > >hotplug_data)); > > + if (IS_ERR(mlxreg_dpu->hotplug)) { > > + err = PTR_ERR(mlxreg_dpu->hotplug); > > + goto fail_register_hotplug; > > + } > > + } > > + > > + return 0; > > + > > +fail_register_hotplug: > > + platform_device_unregister(mlxreg_dpu->io_regs); > > + > > + return err; > > +} > > + > > +static void mlxreg_dpu_config_exit(struct mlxreg_dpu *mlxreg_dpu) > > +{ > > + /* Unregister hotplug driver. */ > > + platform_device_unregister(mlxreg_dpu->hotplug); > > + /* Unregister IO access driver. */ > > + platform_device_unregister(mlxreg_dpu->io_regs); > > +} > > + > > +static int mlxreg_dpu_probe(struct platform_device *pdev) > > +{ > > + struct mlxreg_core_data *data; > > + struct mlxreg_dpu *mlxreg_dpu; > > + void *regmap; > > + int err; > > + > > + data = dev_get_platdata(&pdev->dev); > > + if (!data || !data->hpdev.brdinfo) > > + return -EINVAL; > > + > > + mlxreg_dpu = devm_kzalloc(&pdev->dev, sizeof(*mlxreg_dpu), > GFP_KERNEL); > > + if (!mlxreg_dpu) > > + return -ENOMEM; > > + > > + data->hpdev.adapter = i2c_get_adapter(data->hpdev.nr); > > + if (!data->hpdev.adapter) > > + return -EPROBE_DEFER; > > + > > + /* Create device at the top of DPU I2C tree.*/ > > + data->hpdev.client = i2c_new_client_device(data->hpdev.adapter, > > + data->hpdev.brdinfo); > > + if (IS_ERR(data->hpdev.client)) { > > + dev_err(&pdev->dev, "Failed to create client %s at bus %d at > addr 0x%02x\n", > > + data->hpdev.brdinfo->type, data->hpdev.nr, data- > >hpdev.brdinfo->addr); > > + err = PTR_ERR(data->hpdev.client); > > + goto i2c_new_device_fail; > > + } > > + > > + regmap = devm_regmap_init_i2c(data->hpdev.client, > &mlxreg_dpu_regmap_conf); > > + if (IS_ERR(regmap)) { > > + dev_err(&pdev->dev, "Failed to create regmap for client %s at > bus %d at addr 0x%02x\n", > > + data->hpdev.brdinfo->type, data->hpdev.nr, data- > >hpdev.brdinfo->addr); > > + err = PTR_ERR(regmap); > > + goto devm_regmap_init_i2c_fail; > > + } > > + > > + /* Sync registers with hardware. */ > > + regcache_mark_dirty(regmap); > > + err = regcache_sync(regmap); > > + if (err) { > > + dev_err(&pdev->dev, "Failed to sync regmap for client %s at > bus %d at addr 0x%02x\n", > > + data->hpdev.brdinfo->type, data->hpdev.nr, data- > >hpdev.brdinfo->addr); > > + err = PTR_ERR(regmap); > > + goto regcache_sync_fail; > > + } > > + > > + mlxreg_dpu->data = data; > > + mlxreg_dpu->dev = &pdev->dev; > > + platform_set_drvdata(pdev, mlxreg_dpu); > > + > > + /* Configure DPU. */ > > + err = mlxreg_dpu_config_init(mlxreg_dpu, regmap, data, data- > >hpdev.brdinfo->irq); > > + if (err) > > + goto mlxreg_dpu_config_init_fail; > > + > > + return err; > > + > > +mlxreg_dpu_config_init_fail: > > +regcache_sync_fail: > > +devm_regmap_init_i2c_fail: > > + if (data->hpdev.client) { > > + i2c_unregister_device(data->hpdev.client); > > + data->hpdev.client = NULL; > > + } > > +i2c_new_device_fail: > > + i2c_put_adapter(data->hpdev.adapter); > > + data->hpdev.adapter = NULL; > > + return err; > > +} > > + > > +static void mlxreg_dpu_remove(struct platform_device *pdev) > > +{ > > + struct mlxreg_core_data *data = dev_get_platdata(&pdev->dev); > > + struct mlxreg_dpu *mlxreg_dpu = platform_get_drvdata(pdev); > > + > > + mlxreg_dpu_config_exit(mlxreg_dpu); > > + if (data->hpdev.client) { > > + i2c_unregister_device(data->hpdev.client); > > + data->hpdev.client = NULL; > > + i2c_put_adapter(data->hpdev.adapter); > > + data->hpdev.adapter = NULL; > > + } > > +} > > + > > +static struct platform_driver mlxreg_dpu_driver = { > > + .probe = mlxreg_dpu_probe, > > + .remove = mlxreg_dpu_remove, > > + .driver = { > > + .name = "mlxreg-dpu", > > + }, > > +}; > > + > > +module_platform_driver(mlxreg_dpu_driver); > > + > > +MODULE_AUTHOR("Vadim Pasternak <vadimp@xxxxxxxxxx>"); > > +MODULE_DESCRIPTION("Nvidia Data Processor Unit platform driver"); > > +MODULE_LICENSE("Dual BSD/GPL"); > > +MODULE_ALIAS("platform:mlxreg-dpu"); > >