Message ID | 20250124172632.22437-8-vadimp@nvidia.com (mailing list archive) |
---|---|
State | Changes Requested, archived |
Headers | show |
Series | platform/mellanox: Add support for new systems, amendments, relocate mlx-platform module | expand |
On Fri, 24 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@nvidia.com> > Signed-off-by: Vadim Pasternak <vadimp@nvidia.com> > --- > v4->v5 > Comments pointed out by Ilpo: > - Add empty line in mlxreg_dpu_config_init(). > - Include 'dev_printk.h' from dev_err(). > - Remove unnecessary comments from mlxreg_dpu_config_exit(). > - Put defer probing test in mlxreg_dpu_probe() before allocation. > - Remove unnecessary comments from mlxreg_dpu_probe(). > > v3->v4 > Comments pointed out by Ilpo: > - Fix method of duplication data. > - Rename 'count' to 'item_count'. > > v2->v3 > 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 | 620 +++++++++++++++++++++++++ > 3 files changed, 633 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..bd5fe24fc2b6 > --- /dev/null > +++ b/drivers/platform/mellanox/mlxreg-dpu.c > @@ -0,0 +1,620 @@ > +// SPDX-License-Identifier: GPL-2.0+ > +/* > + * Nvidia Data Processor Unit platform driver > + * > + * Copyright (C) 2025 Nvidia Technologies Ltd. > + */ > + > +#include <linux/device.h> > +#include <linux/dev_printk.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, > + .count = ARRAY_SIZE(mlxreg_dpu_hotplug_items), > + .cell = MLXREG_DPU_REG_AGGRCO_OFFSET, > + .mask = MLXREG_DPU_AGGR_MASK, > +}; > + > +/* mlxreg_dpu - device private data Just make this conform to kerneldoc formatting with: /* * struct mlxreg_dpu - device private data I leave adding the second * to actually enable kerneldoc for this struct is up to your discretion. > + * @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; > + > + mlxreg_dpu->hotplug_data->items = devm_kmemdup(dev, hotplug_data->items, > + mlxreg_dpu->hotplug_data->count * > + sizeof(*mlxreg_dpu->hotplug_data->items), > + GFP_KERNEL); > + if (!mlxreg_dpu->hotplug_data->items) > + return -ENOMEM; > + > + item = mlxreg_dpu->hotplug_data->items; > + for (i = 0; i < hotplug_data->count; i++, item++) { > + 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; > +} > + > +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); Those two comments can be removed. Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
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..bd5fe24fc2b6 --- /dev/null +++ b/drivers/platform/mellanox/mlxreg-dpu.c @@ -0,0 +1,620 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Nvidia Data Processor Unit platform driver + * + * Copyright (C) 2025 Nvidia Technologies Ltd. + */ + +#include <linux/device.h> +#include <linux/dev_printk.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, + .count = 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; + + mlxreg_dpu->hotplug_data->items = devm_kmemdup(dev, hotplug_data->items, + mlxreg_dpu->hotplug_data->count * + sizeof(*mlxreg_dpu->hotplug_data->items), + GFP_KERNEL); + if (!mlxreg_dpu->hotplug_data->items) + return -ENOMEM; + + item = mlxreg_dpu->hotplug_data->items; + for (i = 0; i < hotplug_data->count; i++, item++) { + 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; +} + +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; + + data->hpdev.adapter = i2c_get_adapter(data->hpdev.nr); + if (!data->hpdev.adapter) + return -EPROBE_DEFER; + + mlxreg_dpu = devm_kzalloc(&pdev->dev, sizeof(*mlxreg_dpu), GFP_KERNEL); + if (!mlxreg_dpu) + return -ENOMEM; + + /* 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); + + 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@nvidia.com>"); +MODULE_DESCRIPTION("Nvidia Data Processor Unit platform driver"); +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_ALIAS("platform:mlxreg-dpu");