| // 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)) { |
| dev_err(dev, "Failed to create region 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) |
| { |
| 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); |
| if (!mlxreg_dpu) { |
| err = -ENOMEM; |
| goto alloc_fail; |
| } |
| |
| /* 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: |
| alloc_fail: |
| 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"); |