2025-04-21 12:20:46 +03:00
|
|
|
// 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,
|
|
|
|
.bit = 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_GP1_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 = 0,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.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 = 0,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
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,
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* struct 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)) {
|
2025-06-22 00:29:11 -07:00
|
|
|
dev_err(dev, "Failed to create region for client %s at bus %d at addr 0x%02x\n",
|
2025-04-21 12:20:46 +03:00
|
|
|
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)
|
|
|
|
{
|
|
|
|
platform_device_unregister(mlxreg_dpu->hotplug);
|
|
|
|
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);
|
2025-05-08 23:31:39 +03:00
|
|
|
if (!mlxreg_dpu) {
|
|
|
|
err = -ENOMEM;
|
|
|
|
goto alloc_fail;
|
|
|
|
}
|
2025-04-21 12:20:46 +03:00
|
|
|
|
|
|
|
/* 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);
|
|
|
|
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:
|
|
|
|
i2c_unregister_device(data->hpdev.client);
|
|
|
|
i2c_new_device_fail:
|
2025-05-08 23:31:39 +03:00
|
|
|
alloc_fail:
|
2025-04-21 12:20:46 +03:00
|
|
|
i2c_put_adapter(data->hpdev.adapter);
|
|
|
|
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);
|
|
|
|
i2c_unregister_device(data->hpdev.client);
|
|
|
|
i2c_put_adapter(data->hpdev.adapter);
|
|
|
|
}
|
|
|
|
|
|
|
|
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");
|