Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

platform/mellanox: mlxreg-dpu: Add initial support for Nvidia DPU

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.

Driver 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>
[ij: added depends on I2C]
Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
Link: https://lore.kernel.org/r/20250421092051.7687-2-vadimp@nvidia.com
Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>

authored by

Vadim Pasternak and committed by
Ilpo Järvinen
d00f779e 25b5095a

+625
+13
drivers/platform/mellanox/Kconfig
··· 27 27 28 28 If you have a Mellanox system, say Y or M here. 29 29 30 + config MLXREG_DPU 31 + tristate "Nvidia Data Processor Unit platform driver support" 32 + depends on I2C 33 + select REGMAP_I2C 34 + help 35 + This driver provides support for the Nvidia BF3 Data Processor Units, 36 + which are the part of SN4280 Ethernet smart switch systems 37 + providing a high performance switching solution for Enterprise Data 38 + Centers (EDC) for building Ethernet based clusters, High-Performance 39 + Computing (HPC) and embedded environments. 40 + 41 + If you have a Nvidia smart switch system, say Y or M here. 42 + 30 43 config MLXREG_HOTPLUG 31 44 tristate "Mellanox platform hotplug driver support" 32 45 depends on HWMON
+1
drivers/platform/mellanox/Makefile
··· 7 7 obj-$(CONFIG_MLXBF_BOOTCTL) += mlxbf-bootctl.o 8 8 obj-$(CONFIG_MLXBF_PMC) += mlxbf-pmc.o 9 9 obj-$(CONFIG_MLXBF_TMFIFO) += mlxbf-tmfifo.o 10 + obj-$(CONFIG_MLXREG_DPU) += mlxreg-dpu.o 10 11 obj-$(CONFIG_MLXREG_HOTPLUG) += mlxreg-hotplug.o 11 12 obj-$(CONFIG_MLXREG_IO) += mlxreg-io.o 12 13 obj-$(CONFIG_MLXREG_LC) += mlxreg-lc.o
+611
drivers/platform/mellanox/mlxreg-dpu.c
··· 1 + // SPDX-License-Identifier: GPL-2.0+ 2 + /* 3 + * Nvidia Data Processor Unit platform driver 4 + * 5 + * Copyright (C) 2025 Nvidia Technologies Ltd. 6 + */ 7 + 8 + #include <linux/device.h> 9 + #include <linux/dev_printk.h> 10 + #include <linux/i2c.h> 11 + #include <linux/module.h> 12 + #include <linux/platform_data/mlxcpld.h> 13 + #include <linux/platform_data/mlxreg.h> 14 + #include <linux/platform_device.h> 15 + #include <linux/regmap.h> 16 + 17 + /* I2C bus IO offsets */ 18 + #define MLXREG_DPU_REG_FPGA1_VER_OFFSET 0x2400 19 + #define MLXREG_DPU_REG_FPGA1_PN_OFFSET 0x2404 20 + #define MLXREG_DPU_REG_FPGA1_PN1_OFFSET 0x2405 21 + #define MLXREG_DPU_REG_PG_OFFSET 0x2414 22 + #define MLXREG_DPU_REG_PG_EVENT_OFFSET 0x2415 23 + #define MLXREG_DPU_REG_PG_MASK_OFFSET 0x2416 24 + #define MLXREG_DPU_REG_RESET_GP1_OFFSET 0x2417 25 + #define MLXREG_DPU_REG_RST_CAUSE1_OFFSET 0x241e 26 + #define MLXREG_DPU_REG_GP0_RO_OFFSET 0x242b 27 + #define MLXREG_DPU_REG_GP0_OFFSET 0x242e 28 + #define MLXREG_DPU_REG_GP1_OFFSET 0x242c 29 + #define MLXREG_DPU_REG_GP4_OFFSET 0x2438 30 + #define MLXREG_DPU_REG_AGGRCO_OFFSET 0x2442 31 + #define MLXREG_DPU_REG_AGGRCO_MASK_OFFSET 0x2443 32 + #define MLXREG_DPU_REG_HEALTH_OFFSET 0x244d 33 + #define MLXREG_DPU_REG_HEALTH_EVENT_OFFSET 0x244e 34 + #define MLXREG_DPU_REG_HEALTH_MASK_OFFSET 0x244f 35 + #define MLXREG_DPU_REG_FPGA1_MVER_OFFSET 0x24de 36 + #define MLXREG_DPU_REG_CONFIG3_OFFSET 0x24fd 37 + #define MLXREG_DPU_REG_MAX 0x3fff 38 + 39 + /* Power Good event masks. */ 40 + #define MLXREG_DPU_PG_VDDIO_MASK BIT(0) 41 + #define MLXREG_DPU_PG_VDD_CPU_MASK BIT(1) 42 + #define MLXREG_DPU_PG_VDD_MASK BIT(2) 43 + #define MLXREG_DPU_PG_1V8_MASK BIT(3) 44 + #define MLXREG_DPU_PG_COMPARATOR_MASK BIT(4) 45 + #define MLXREG_DPU_PG_VDDQ_MASK BIT(5) 46 + #define MLXREG_DPU_PG_HVDD_MASK BIT(6) 47 + #define MLXREG_DPU_PG_DVDD_MASK BIT(7) 48 + #define MLXREG_DPU_PG_MASK (MLXREG_DPU_PG_DVDD_MASK | \ 49 + MLXREG_DPU_PG_HVDD_MASK | \ 50 + MLXREG_DPU_PG_VDDQ_MASK | \ 51 + MLXREG_DPU_PG_COMPARATOR_MASK | \ 52 + MLXREG_DPU_PG_1V8_MASK | \ 53 + MLXREG_DPU_PG_VDD_CPU_MASK | \ 54 + MLXREG_DPU_PG_VDD_MASK | \ 55 + MLXREG_DPU_PG_VDDIO_MASK) 56 + 57 + /* Health event masks. */ 58 + #define MLXREG_DPU_HLTH_THERMAL_TRIP_MASK BIT(0) 59 + #define MLXREG_DPU_HLTH_UFM_UPGRADE_DONE_MASK BIT(1) 60 + #define MLXREG_DPU_HLTH_VDDQ_HOT_ALERT_MASK BIT(2) 61 + #define MLXREG_DPU_HLTH_VDD_CPU_HOT_ALERT_MASK BIT(3) 62 + #define MLXREG_DPU_HLTH_VDDQ_ALERT_MASK BIT(4) 63 + #define MLXREG_DPU_HLTH_VDD_CPU_ALERT_MASK BIT(5) 64 + #define MLXREG_DPU_HEALTH_MASK (MLXREG_DPU_HLTH_UFM_UPGRADE_DONE_MASK | \ 65 + MLXREG_DPU_HLTH_VDDQ_HOT_ALERT_MASK | \ 66 + MLXREG_DPU_HLTH_VDD_CPU_HOT_ALERT_MASK | \ 67 + MLXREG_DPU_HLTH_VDDQ_ALERT_MASK | \ 68 + MLXREG_DPU_HLTH_VDD_CPU_ALERT_MASK | \ 69 + MLXREG_DPU_HLTH_THERMAL_TRIP_MASK) 70 + 71 + /* Hotplug aggregation masks. */ 72 + #define MLXREG_DPU_HEALTH_AGGR_MASK BIT(0) 73 + #define MLXREG_DPU_PG_AGGR_MASK BIT(1) 74 + #define MLXREG_DPU_AGGR_MASK (MLXREG_DPU_HEALTH_AGGR_MASK | \ 75 + MLXREG_DPU_PG_AGGR_MASK) 76 + 77 + /* Voltage regulator firmware update status mask. */ 78 + #define MLXREG_DPU_VOLTREG_UPD_MASK GENMASK(5, 4) 79 + 80 + #define MLXREG_DPU_NR_NONE (-1) 81 + 82 + /* 83 + * enum mlxreg_dpu_type - Data Processor Unit types 84 + * 85 + * @MLXREG_DPU_BF3: DPU equipped with BF3 SoC; 86 + */ 87 + enum mlxreg_dpu_type { 88 + MLXREG_DPU_BF3 = 0x0050, 89 + }; 90 + 91 + /* Default register access data. */ 92 + static struct mlxreg_core_data mlxreg_dpu_io_data[] = { 93 + { 94 + .label = "fpga1_version", 95 + .reg = MLXREG_DPU_REG_FPGA1_VER_OFFSET, 96 + .bit = GENMASK(7, 0), 97 + .mode = 0444, 98 + }, 99 + { 100 + .label = "fpga1_pn", 101 + .reg = MLXREG_DPU_REG_FPGA1_PN_OFFSET, 102 + .bit = GENMASK(15, 0), 103 + .mode = 0444, 104 + .regnum = 2, 105 + }, 106 + { 107 + .label = "fpga1_version_min", 108 + .reg = MLXREG_DPU_REG_FPGA1_MVER_OFFSET, 109 + .bit = GENMASK(7, 0), 110 + .mode = 0444, 111 + }, 112 + { 113 + .label = "perst_rst", 114 + .reg = MLXREG_DPU_REG_RESET_GP1_OFFSET, 115 + .mask = GENMASK(7, 0) & ~BIT(0), 116 + .mode = 0644, 117 + }, 118 + { 119 + .label = "usbphy_rst", 120 + .reg = MLXREG_DPU_REG_RESET_GP1_OFFSET, 121 + .mask = GENMASK(7, 0) & ~BIT(1), 122 + .mode = 0644, 123 + }, 124 + { 125 + .label = "phy_rst", 126 + .reg = MLXREG_DPU_REG_RESET_GP1_OFFSET, 127 + .mask = GENMASK(7, 0) & ~BIT(2), 128 + .mode = 0644, 129 + }, 130 + { 131 + .label = "tpm_rst", 132 + .reg = MLXREG_DPU_REG_RESET_GP1_OFFSET, 133 + .mask = GENMASK(7, 0) & ~BIT(6), 134 + .mode = 0644, 135 + }, 136 + { 137 + .label = "reset_from_main_board", 138 + .reg = MLXREG_DPU_REG_RST_CAUSE1_OFFSET, 139 + .mask = GENMASK(7, 0) & ~BIT(1), 140 + .mode = 0444, 141 + }, 142 + { 143 + .label = "reset_aux_pwr_or_reload", 144 + .reg = MLXREG_DPU_REG_RST_CAUSE1_OFFSET, 145 + .mask = GENMASK(7, 0) & ~BIT(2), 146 + .mode = 0444, 147 + }, 148 + { 149 + .label = "reset_comex_pwr_fail", 150 + .reg = MLXREG_DPU_REG_RST_CAUSE1_OFFSET, 151 + .mask = GENMASK(7, 0) & ~BIT(3), 152 + .mode = 0444, 153 + }, 154 + { 155 + .label = "reset_dpu_thermal", 156 + .reg = MLXREG_DPU_REG_RST_CAUSE1_OFFSET, 157 + .mask = GENMASK(7, 0) & ~BIT(6), 158 + .mode = 0444, 159 + }, 160 + { 161 + .label = "reset_pwr_off", 162 + .reg = MLXREG_DPU_REG_RST_CAUSE1_OFFSET, 163 + .mask = GENMASK(7, 0) & ~BIT(7), 164 + .mode = 0444, 165 + }, 166 + { 167 + .label = "dpu_id", 168 + .reg = MLXREG_DPU_REG_GP0_RO_OFFSET, 169 + .bit = GENMASK(3, 0), 170 + .mode = 0444, 171 + }, 172 + { 173 + .label = "voltreg_update_status", 174 + .reg = MLXREG_DPU_REG_GP0_RO_OFFSET, 175 + .mask = MLXREG_DPU_VOLTREG_UPD_MASK, 176 + .bit = 5, 177 + .mode = 0444, 178 + }, 179 + { 180 + .label = "boot_progress", 181 + .reg = MLXREG_DPU_REG_GP1_OFFSET, 182 + .mask = GENMASK(3, 0), 183 + .mode = 0444, 184 + }, 185 + { 186 + .label = "ufm_upgrade", 187 + .reg = MLXREG_DPU_REG_GP4_OFFSET, 188 + .mask = GENMASK(7, 0) & ~BIT(1), 189 + .mode = 0644, 190 + }, 191 + }; 192 + 193 + static struct mlxreg_core_platform_data mlxreg_dpu_default_regs_io_data = { 194 + .data = mlxreg_dpu_io_data, 195 + .counter = ARRAY_SIZE(mlxreg_dpu_io_data), 196 + }; 197 + 198 + /* Default hotplug data. */ 199 + static struct mlxreg_core_data mlxreg_dpu_power_events_items_data[] = { 200 + { 201 + .label = "pg_vddio", 202 + .reg = MLXREG_DPU_REG_PG_OFFSET, 203 + .mask = MLXREG_DPU_PG_VDDIO_MASK, 204 + .hpdev.nr = MLXREG_DPU_NR_NONE, 205 + }, 206 + { 207 + .label = "pg_vdd_cpu", 208 + .reg = MLXREG_DPU_REG_PG_OFFSET, 209 + .mask = MLXREG_DPU_PG_VDD_CPU_MASK, 210 + .hpdev.nr = MLXREG_DPU_NR_NONE, 211 + }, 212 + { 213 + .label = "pg_vdd", 214 + .reg = MLXREG_DPU_REG_PG_OFFSET, 215 + .mask = MLXREG_DPU_PG_VDD_MASK, 216 + .hpdev.nr = MLXREG_DPU_NR_NONE, 217 + }, 218 + { 219 + .label = "pg_1v8", 220 + .reg = MLXREG_DPU_REG_PG_OFFSET, 221 + .mask = MLXREG_DPU_PG_1V8_MASK, 222 + .hpdev.nr = MLXREG_DPU_NR_NONE, 223 + }, 224 + { 225 + .label = "pg_comparator", 226 + .reg = MLXREG_DPU_REG_PG_OFFSET, 227 + .mask = MLXREG_DPU_PG_COMPARATOR_MASK, 228 + .hpdev.nr = MLXREG_DPU_NR_NONE, 229 + }, 230 + { 231 + .label = "pg_vddq", 232 + .reg = MLXREG_DPU_REG_PG_OFFSET, 233 + .mask = MLXREG_DPU_PG_VDDQ_MASK, 234 + .hpdev.nr = MLXREG_DPU_NR_NONE, 235 + }, 236 + { 237 + .label = "pg_hvdd", 238 + .reg = MLXREG_DPU_REG_PG_OFFSET, 239 + .mask = MLXREG_DPU_PG_HVDD_MASK, 240 + .hpdev.nr = MLXREG_DPU_NR_NONE, 241 + }, 242 + { 243 + .label = "pg_dvdd", 244 + .reg = MLXREG_DPU_REG_PG_OFFSET, 245 + .mask = MLXREG_DPU_PG_DVDD_MASK, 246 + .hpdev.nr = MLXREG_DPU_NR_NONE, 247 + }, 248 + }; 249 + 250 + static struct mlxreg_core_data mlxreg_dpu_health_events_items_data[] = { 251 + { 252 + .label = "thermal_trip", 253 + .reg = MLXREG_DPU_REG_HEALTH_OFFSET, 254 + .mask = MLXREG_DPU_HLTH_THERMAL_TRIP_MASK, 255 + .hpdev.nr = MLXREG_DPU_NR_NONE, 256 + }, 257 + { 258 + .label = "ufm_upgrade_done", 259 + .reg = MLXREG_DPU_REG_HEALTH_OFFSET, 260 + .mask = MLXREG_DPU_HLTH_UFM_UPGRADE_DONE_MASK, 261 + .hpdev.nr = MLXREG_DPU_NR_NONE, 262 + }, 263 + { 264 + .label = "vddq_hot_alert", 265 + .reg = MLXREG_DPU_REG_HEALTH_OFFSET, 266 + .mask = MLXREG_DPU_HLTH_VDDQ_HOT_ALERT_MASK, 267 + .hpdev.nr = MLXREG_DPU_NR_NONE, 268 + }, 269 + { 270 + .label = "vdd_cpu_hot_alert", 271 + .reg = MLXREG_DPU_REG_HEALTH_OFFSET, 272 + .mask = MLXREG_DPU_HLTH_VDD_CPU_HOT_ALERT_MASK, 273 + .hpdev.nr = MLXREG_DPU_NR_NONE, 274 + }, 275 + { 276 + .label = "vddq_alert", 277 + .reg = MLXREG_DPU_REG_HEALTH_OFFSET, 278 + .mask = MLXREG_DPU_HLTH_VDDQ_ALERT_MASK, 279 + .hpdev.nr = MLXREG_DPU_NR_NONE, 280 + }, 281 + { 282 + .label = "vdd_cpu_alert", 283 + .reg = MLXREG_DPU_REG_HEALTH_OFFSET, 284 + .mask = MLXREG_DPU_HLTH_VDD_CPU_ALERT_MASK, 285 + .hpdev.nr = MLXREG_DPU_NR_NONE, 286 + }, 287 + }; 288 + 289 + static struct mlxreg_core_item mlxreg_dpu_hotplug_items[] = { 290 + { 291 + .data = mlxreg_dpu_power_events_items_data, 292 + .aggr_mask = MLXREG_DPU_PG_AGGR_MASK, 293 + .reg = MLXREG_DPU_REG_PG_OFFSET, 294 + .mask = MLXREG_DPU_PG_MASK, 295 + .count = ARRAY_SIZE(mlxreg_dpu_power_events_items_data), 296 + .health = false, 297 + .inversed = 0, 298 + }, 299 + { 300 + .data = mlxreg_dpu_health_events_items_data, 301 + .aggr_mask = MLXREG_DPU_HEALTH_AGGR_MASK, 302 + .reg = MLXREG_DPU_REG_HEALTH_OFFSET, 303 + .mask = MLXREG_DPU_HEALTH_MASK, 304 + .count = ARRAY_SIZE(mlxreg_dpu_health_events_items_data), 305 + .health = false, 306 + .inversed = 0, 307 + }, 308 + }; 309 + 310 + static 311 + struct mlxreg_core_hotplug_platform_data mlxreg_dpu_default_hotplug_data = { 312 + .items = mlxreg_dpu_hotplug_items, 313 + .count = ARRAY_SIZE(mlxreg_dpu_hotplug_items), 314 + .cell = MLXREG_DPU_REG_AGGRCO_OFFSET, 315 + .mask = MLXREG_DPU_AGGR_MASK, 316 + }; 317 + 318 + /** 319 + * struct mlxreg_dpu - device private data 320 + * @dev: platform device 321 + * @data: platform core data 322 + * @io_data: register access platform data 323 + * @io_regs: register access device 324 + * @hotplug_data: hotplug platform data 325 + * @hotplug: hotplug device 326 + */ 327 + struct mlxreg_dpu { 328 + struct device *dev; 329 + struct mlxreg_core_data *data; 330 + struct mlxreg_core_platform_data *io_data; 331 + struct platform_device *io_regs; 332 + struct mlxreg_core_hotplug_platform_data *hotplug_data; 333 + struct platform_device *hotplug; 334 + }; 335 + 336 + static bool mlxreg_dpu_writeable_reg(struct device *dev, unsigned int reg) 337 + { 338 + switch (reg) { 339 + case MLXREG_DPU_REG_PG_EVENT_OFFSET: 340 + case MLXREG_DPU_REG_PG_MASK_OFFSET: 341 + case MLXREG_DPU_REG_RESET_GP1_OFFSET: 342 + case MLXREG_DPU_REG_GP0_OFFSET: 343 + case MLXREG_DPU_REG_GP1_OFFSET: 344 + case MLXREG_DPU_REG_GP4_OFFSET: 345 + case MLXREG_DPU_REG_AGGRCO_OFFSET: 346 + case MLXREG_DPU_REG_AGGRCO_MASK_OFFSET: 347 + case MLXREG_DPU_REG_HEALTH_EVENT_OFFSET: 348 + case MLXREG_DPU_REG_HEALTH_MASK_OFFSET: 349 + return true; 350 + } 351 + return false; 352 + } 353 + 354 + static bool mlxreg_dpu_readable_reg(struct device *dev, unsigned int reg) 355 + { 356 + switch (reg) { 357 + case MLXREG_DPU_REG_FPGA1_VER_OFFSET: 358 + case MLXREG_DPU_REG_FPGA1_PN_OFFSET: 359 + case MLXREG_DPU_REG_FPGA1_PN1_OFFSET: 360 + case MLXREG_DPU_REG_PG_OFFSET: 361 + case MLXREG_DPU_REG_PG_EVENT_OFFSET: 362 + case MLXREG_DPU_REG_PG_MASK_OFFSET: 363 + case MLXREG_DPU_REG_RESET_GP1_OFFSET: 364 + case MLXREG_DPU_REG_RST_CAUSE1_OFFSET: 365 + case MLXREG_DPU_REG_GP0_RO_OFFSET: 366 + case MLXREG_DPU_REG_GP0_OFFSET: 367 + case MLXREG_DPU_REG_GP1_OFFSET: 368 + case MLXREG_DPU_REG_GP4_OFFSET: 369 + case MLXREG_DPU_REG_AGGRCO_OFFSET: 370 + case MLXREG_DPU_REG_AGGRCO_MASK_OFFSET: 371 + case MLXREG_DPU_REG_HEALTH_OFFSET: 372 + case MLXREG_DPU_REG_HEALTH_EVENT_OFFSET: 373 + case MLXREG_DPU_REG_HEALTH_MASK_OFFSET: 374 + case MLXREG_DPU_REG_FPGA1_MVER_OFFSET: 375 + case MLXREG_DPU_REG_CONFIG3_OFFSET: 376 + return true; 377 + } 378 + return false; 379 + } 380 + 381 + static bool mlxreg_dpu_volatile_reg(struct device *dev, unsigned int reg) 382 + { 383 + switch (reg) { 384 + case MLXREG_DPU_REG_FPGA1_VER_OFFSET: 385 + case MLXREG_DPU_REG_FPGA1_PN_OFFSET: 386 + case MLXREG_DPU_REG_FPGA1_PN1_OFFSET: 387 + case MLXREG_DPU_REG_PG_OFFSET: 388 + case MLXREG_DPU_REG_PG_EVENT_OFFSET: 389 + case MLXREG_DPU_REG_PG_MASK_OFFSET: 390 + case MLXREG_DPU_REG_RESET_GP1_OFFSET: 391 + case MLXREG_DPU_REG_RST_CAUSE1_OFFSET: 392 + case MLXREG_DPU_REG_GP0_RO_OFFSET: 393 + case MLXREG_DPU_REG_GP0_OFFSET: 394 + case MLXREG_DPU_REG_GP1_OFFSET: 395 + case MLXREG_DPU_REG_GP4_OFFSET: 396 + case MLXREG_DPU_REG_AGGRCO_OFFSET: 397 + case MLXREG_DPU_REG_AGGRCO_MASK_OFFSET: 398 + case MLXREG_DPU_REG_HEALTH_OFFSET: 399 + case MLXREG_DPU_REG_HEALTH_EVENT_OFFSET: 400 + case MLXREG_DPU_REG_HEALTH_MASK_OFFSET: 401 + case MLXREG_DPU_REG_FPGA1_MVER_OFFSET: 402 + case MLXREG_DPU_REG_CONFIG3_OFFSET: 403 + return true; 404 + } 405 + return false; 406 + } 407 + 408 + /* Configuration for the register map of a device with 2 bytes address space. */ 409 + static const struct regmap_config mlxreg_dpu_regmap_conf = { 410 + .reg_bits = 16, 411 + .val_bits = 8, 412 + .max_register = MLXREG_DPU_REG_MAX, 413 + .cache_type = REGCACHE_FLAT, 414 + .writeable_reg = mlxreg_dpu_writeable_reg, 415 + .readable_reg = mlxreg_dpu_readable_reg, 416 + .volatile_reg = mlxreg_dpu_volatile_reg, 417 + }; 418 + 419 + static int 420 + mlxreg_dpu_copy_hotplug_data(struct device *dev, struct mlxreg_dpu *mlxreg_dpu, 421 + const struct mlxreg_core_hotplug_platform_data *hotplug_data) 422 + { 423 + struct mlxreg_core_item *item; 424 + int i; 425 + 426 + mlxreg_dpu->hotplug_data = devm_kmemdup(dev, hotplug_data, 427 + sizeof(*mlxreg_dpu->hotplug_data), GFP_KERNEL); 428 + if (!mlxreg_dpu->hotplug_data) 429 + return -ENOMEM; 430 + 431 + mlxreg_dpu->hotplug_data->items = devm_kmemdup(dev, hotplug_data->items, 432 + mlxreg_dpu->hotplug_data->count * 433 + sizeof(*mlxreg_dpu->hotplug_data->items), 434 + GFP_KERNEL); 435 + if (!mlxreg_dpu->hotplug_data->items) 436 + return -ENOMEM; 437 + 438 + item = mlxreg_dpu->hotplug_data->items; 439 + for (i = 0; i < hotplug_data->count; i++, item++) { 440 + item->data = devm_kmemdup(dev, hotplug_data->items[i].data, 441 + hotplug_data->items[i].count * sizeof(*item->data), 442 + GFP_KERNEL); 443 + if (!item->data) 444 + return -ENOMEM; 445 + } 446 + 447 + return 0; 448 + } 449 + 450 + static int mlxreg_dpu_config_init(struct mlxreg_dpu *mlxreg_dpu, void *regmap, 451 + struct mlxreg_core_data *data, int irq) 452 + { 453 + struct device *dev = &data->hpdev.client->dev; 454 + u32 regval; 455 + int err; 456 + 457 + /* Validate DPU type. */ 458 + err = regmap_read(regmap, MLXREG_DPU_REG_CONFIG3_OFFSET, &regval); 459 + if (err) 460 + return err; 461 + 462 + switch (regval) { 463 + case MLXREG_DPU_BF3: 464 + /* Copy platform specific hotplug data. */ 465 + err = mlxreg_dpu_copy_hotplug_data(dev, mlxreg_dpu, 466 + &mlxreg_dpu_default_hotplug_data); 467 + if (err) 468 + return err; 469 + 470 + mlxreg_dpu->io_data = &mlxreg_dpu_default_regs_io_data; 471 + 472 + break; 473 + default: 474 + return -ENODEV; 475 + } 476 + 477 + /* Register IO access driver. */ 478 + if (mlxreg_dpu->io_data) { 479 + mlxreg_dpu->io_data->regmap = regmap; 480 + mlxreg_dpu->io_regs = 481 + platform_device_register_resndata(dev, "mlxreg-io", 482 + data->slot, NULL, 0, 483 + mlxreg_dpu->io_data, 484 + sizeof(*mlxreg_dpu->io_data)); 485 + if (IS_ERR(mlxreg_dpu->io_regs)) { 486 + dev_err(dev, "Failed to create regio for client %s at bus %d at addr 0x%02x\n", 487 + data->hpdev.brdinfo->type, data->hpdev.nr, 488 + data->hpdev.brdinfo->addr); 489 + return PTR_ERR(mlxreg_dpu->io_regs); 490 + } 491 + } 492 + 493 + /* Register hotplug driver. */ 494 + if (mlxreg_dpu->hotplug_data && irq) { 495 + mlxreg_dpu->hotplug_data->regmap = regmap; 496 + mlxreg_dpu->hotplug_data->irq = irq; 497 + mlxreg_dpu->hotplug = 498 + platform_device_register_resndata(dev, "mlxreg-hotplug", 499 + data->slot, NULL, 0, 500 + mlxreg_dpu->hotplug_data, 501 + sizeof(*mlxreg_dpu->hotplug_data)); 502 + if (IS_ERR(mlxreg_dpu->hotplug)) { 503 + err = PTR_ERR(mlxreg_dpu->hotplug); 504 + goto fail_register_hotplug; 505 + } 506 + } 507 + 508 + return 0; 509 + 510 + fail_register_hotplug: 511 + platform_device_unregister(mlxreg_dpu->io_regs); 512 + 513 + return err; 514 + } 515 + 516 + static void mlxreg_dpu_config_exit(struct mlxreg_dpu *mlxreg_dpu) 517 + { 518 + platform_device_unregister(mlxreg_dpu->hotplug); 519 + platform_device_unregister(mlxreg_dpu->io_regs); 520 + } 521 + 522 + static int mlxreg_dpu_probe(struct platform_device *pdev) 523 + { 524 + struct mlxreg_core_data *data; 525 + struct mlxreg_dpu *mlxreg_dpu; 526 + void *regmap; 527 + int err; 528 + 529 + data = dev_get_platdata(&pdev->dev); 530 + if (!data || !data->hpdev.brdinfo) 531 + return -EINVAL; 532 + 533 + data->hpdev.adapter = i2c_get_adapter(data->hpdev.nr); 534 + if (!data->hpdev.adapter) 535 + return -EPROBE_DEFER; 536 + 537 + mlxreg_dpu = devm_kzalloc(&pdev->dev, sizeof(*mlxreg_dpu), GFP_KERNEL); 538 + if (!mlxreg_dpu) 539 + return -ENOMEM; 540 + 541 + /* Create device at the top of DPU I2C tree. */ 542 + data->hpdev.client = i2c_new_client_device(data->hpdev.adapter, 543 + data->hpdev.brdinfo); 544 + if (IS_ERR(data->hpdev.client)) { 545 + dev_err(&pdev->dev, "Failed to create client %s at bus %d at addr 0x%02x\n", 546 + data->hpdev.brdinfo->type, data->hpdev.nr, data->hpdev.brdinfo->addr); 547 + err = PTR_ERR(data->hpdev.client); 548 + goto i2c_new_device_fail; 549 + } 550 + 551 + regmap = devm_regmap_init_i2c(data->hpdev.client, &mlxreg_dpu_regmap_conf); 552 + if (IS_ERR(regmap)) { 553 + dev_err(&pdev->dev, "Failed to create regmap for client %s at bus %d at addr 0x%02x\n", 554 + data->hpdev.brdinfo->type, data->hpdev.nr, data->hpdev.brdinfo->addr); 555 + err = PTR_ERR(regmap); 556 + goto devm_regmap_init_i2c_fail; 557 + } 558 + 559 + /* Sync registers with hardware. */ 560 + regcache_mark_dirty(regmap); 561 + err = regcache_sync(regmap); 562 + if (err) { 563 + dev_err(&pdev->dev, "Failed to sync regmap for client %s at bus %d at addr 0x%02x\n", 564 + data->hpdev.brdinfo->type, data->hpdev.nr, data->hpdev.brdinfo->addr); 565 + err = PTR_ERR(regmap); 566 + goto regcache_sync_fail; 567 + } 568 + 569 + mlxreg_dpu->data = data; 570 + mlxreg_dpu->dev = &pdev->dev; 571 + platform_set_drvdata(pdev, mlxreg_dpu); 572 + 573 + err = mlxreg_dpu_config_init(mlxreg_dpu, regmap, data, data->hpdev.brdinfo->irq); 574 + if (err) 575 + goto mlxreg_dpu_config_init_fail; 576 + 577 + return err; 578 + 579 + mlxreg_dpu_config_init_fail: 580 + regcache_sync_fail: 581 + devm_regmap_init_i2c_fail: 582 + i2c_unregister_device(data->hpdev.client); 583 + i2c_new_device_fail: 584 + i2c_put_adapter(data->hpdev.adapter); 585 + return err; 586 + } 587 + 588 + static void mlxreg_dpu_remove(struct platform_device *pdev) 589 + { 590 + struct mlxreg_core_data *data = dev_get_platdata(&pdev->dev); 591 + struct mlxreg_dpu *mlxreg_dpu = platform_get_drvdata(pdev); 592 + 593 + mlxreg_dpu_config_exit(mlxreg_dpu); 594 + i2c_unregister_device(data->hpdev.client); 595 + i2c_put_adapter(data->hpdev.adapter); 596 + } 597 + 598 + static struct platform_driver mlxreg_dpu_driver = { 599 + .probe = mlxreg_dpu_probe, 600 + .remove = mlxreg_dpu_remove, 601 + .driver = { 602 + .name = "mlxreg-dpu", 603 + }, 604 + }; 605 + 606 + module_platform_driver(mlxreg_dpu_driver); 607 + 608 + MODULE_AUTHOR("Vadim Pasternak <vadimp@nvidia.com>"); 609 + MODULE_DESCRIPTION("Nvidia Data Processor Unit platform driver"); 610 + MODULE_LICENSE("Dual BSD/GPL"); 611 + MODULE_ALIAS("platform:mlxreg-dpu");