162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Nvidia line card driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2020 Nvidia Technologies Ltd. 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/device.h> 962306a36Sopenharmony_ci#include <linux/i2c.h> 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci#include <linux/platform_data/mlxcpld.h> 1262306a36Sopenharmony_ci#include <linux/platform_data/mlxreg.h> 1362306a36Sopenharmony_ci#include <linux/platform_device.h> 1462306a36Sopenharmony_ci#include <linux/regmap.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci/* I2C bus IO offsets */ 1762306a36Sopenharmony_ci#define MLXREG_LC_REG_CPLD1_VER_OFFSET 0x2500 1862306a36Sopenharmony_ci#define MLXREG_LC_REG_FPGA1_VER_OFFSET 0x2501 1962306a36Sopenharmony_ci#define MLXREG_LC_REG_CPLD1_PN_OFFSET 0x2504 2062306a36Sopenharmony_ci#define MLXREG_LC_REG_FPGA1_PN_OFFSET 0x2506 2162306a36Sopenharmony_ci#define MLXREG_LC_REG_RESET_CAUSE_OFFSET 0x251d 2262306a36Sopenharmony_ci#define MLXREG_LC_REG_LED1_OFFSET 0x2520 2362306a36Sopenharmony_ci#define MLXREG_LC_REG_GP0_OFFSET 0x252e 2462306a36Sopenharmony_ci#define MLXREG_LC_REG_FIELD_UPGRADE 0x2534 2562306a36Sopenharmony_ci#define MLXREG_LC_CHANNEL_I2C_REG 0x25dc 2662306a36Sopenharmony_ci#define MLXREG_LC_REG_CPLD1_MVER_OFFSET 0x25de 2762306a36Sopenharmony_ci#define MLXREG_LC_REG_FPGA1_MVER_OFFSET 0x25df 2862306a36Sopenharmony_ci#define MLXREG_LC_REG_MAX_POWER_OFFSET 0x25f1 2962306a36Sopenharmony_ci#define MLXREG_LC_REG_CONFIG_OFFSET 0x25fb 3062306a36Sopenharmony_ci#define MLXREG_LC_REG_MAX 0x3fff 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci/** 3362306a36Sopenharmony_ci * enum mlxreg_lc_type - line cards types 3462306a36Sopenharmony_ci * 3562306a36Sopenharmony_ci * @MLXREG_LC_SN4800_C16: 100GbE line card with 16 QSFP28 ports; 3662306a36Sopenharmony_ci */ 3762306a36Sopenharmony_cienum mlxreg_lc_type { 3862306a36Sopenharmony_ci MLXREG_LC_SN4800_C16 = 0x0000, 3962306a36Sopenharmony_ci}; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci/** 4262306a36Sopenharmony_ci * enum mlxreg_lc_state - line cards state 4362306a36Sopenharmony_ci * 4462306a36Sopenharmony_ci * @MLXREG_LC_INITIALIZED: line card is initialized; 4562306a36Sopenharmony_ci * @MLXREG_LC_POWERED: line card is powered; 4662306a36Sopenharmony_ci * @MLXREG_LC_SYNCED: line card is synchronized between hardware and firmware; 4762306a36Sopenharmony_ci */ 4862306a36Sopenharmony_cienum mlxreg_lc_state { 4962306a36Sopenharmony_ci MLXREG_LC_INITIALIZED = BIT(0), 5062306a36Sopenharmony_ci MLXREG_LC_POWERED = BIT(1), 5162306a36Sopenharmony_ci MLXREG_LC_SYNCED = BIT(2), 5262306a36Sopenharmony_ci}; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci#define MLXREG_LC_CONFIGURED (MLXREG_LC_INITIALIZED | MLXREG_LC_POWERED | MLXREG_LC_SYNCED) 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci/* mlxreg_lc - device private data 5762306a36Sopenharmony_ci * @dev: platform device; 5862306a36Sopenharmony_ci * @lock: line card lock; 5962306a36Sopenharmony_ci * @par_regmap: parent device regmap handle; 6062306a36Sopenharmony_ci * @data: pltaform core data; 6162306a36Sopenharmony_ci * @io_data: register access platform data; 6262306a36Sopenharmony_ci * @led_data: LED platform data ; 6362306a36Sopenharmony_ci * @mux_data: MUX platform data; 6462306a36Sopenharmony_ci * @led: LED device; 6562306a36Sopenharmony_ci * @io_regs: register access device; 6662306a36Sopenharmony_ci * @mux_brdinfo: mux configuration; 6762306a36Sopenharmony_ci * @mux: mux devices; 6862306a36Sopenharmony_ci * @aux_devs: I2C devices feeding by auxiliary power; 6962306a36Sopenharmony_ci * @aux_devs_num: number of I2C devices feeding by auxiliary power; 7062306a36Sopenharmony_ci * @main_devs: I2C devices feeding by main power; 7162306a36Sopenharmony_ci * @main_devs_num: number of I2C devices feeding by main power; 7262306a36Sopenharmony_ci * @state: line card state; 7362306a36Sopenharmony_ci */ 7462306a36Sopenharmony_cistruct mlxreg_lc { 7562306a36Sopenharmony_ci struct device *dev; 7662306a36Sopenharmony_ci struct mutex lock; /* line card access lock */ 7762306a36Sopenharmony_ci void *par_regmap; 7862306a36Sopenharmony_ci struct mlxreg_core_data *data; 7962306a36Sopenharmony_ci struct mlxreg_core_platform_data *io_data; 8062306a36Sopenharmony_ci struct mlxreg_core_platform_data *led_data; 8162306a36Sopenharmony_ci struct mlxcpld_mux_plat_data *mux_data; 8262306a36Sopenharmony_ci struct platform_device *led; 8362306a36Sopenharmony_ci struct platform_device *io_regs; 8462306a36Sopenharmony_ci struct i2c_board_info *mux_brdinfo; 8562306a36Sopenharmony_ci struct platform_device *mux; 8662306a36Sopenharmony_ci struct mlxreg_hotplug_device *aux_devs; 8762306a36Sopenharmony_ci int aux_devs_num; 8862306a36Sopenharmony_ci struct mlxreg_hotplug_device *main_devs; 8962306a36Sopenharmony_ci int main_devs_num; 9062306a36Sopenharmony_ci enum mlxreg_lc_state state; 9162306a36Sopenharmony_ci}; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_cistatic bool mlxreg_lc_writeable_reg(struct device *dev, unsigned int reg) 9462306a36Sopenharmony_ci{ 9562306a36Sopenharmony_ci switch (reg) { 9662306a36Sopenharmony_ci case MLXREG_LC_REG_LED1_OFFSET: 9762306a36Sopenharmony_ci case MLXREG_LC_REG_GP0_OFFSET: 9862306a36Sopenharmony_ci case MLXREG_LC_REG_FIELD_UPGRADE: 9962306a36Sopenharmony_ci case MLXREG_LC_CHANNEL_I2C_REG: 10062306a36Sopenharmony_ci return true; 10162306a36Sopenharmony_ci } 10262306a36Sopenharmony_ci return false; 10362306a36Sopenharmony_ci} 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_cistatic bool mlxreg_lc_readable_reg(struct device *dev, unsigned int reg) 10662306a36Sopenharmony_ci{ 10762306a36Sopenharmony_ci switch (reg) { 10862306a36Sopenharmony_ci case MLXREG_LC_REG_CPLD1_VER_OFFSET: 10962306a36Sopenharmony_ci case MLXREG_LC_REG_FPGA1_VER_OFFSET: 11062306a36Sopenharmony_ci case MLXREG_LC_REG_CPLD1_PN_OFFSET: 11162306a36Sopenharmony_ci case MLXREG_LC_REG_FPGA1_PN_OFFSET: 11262306a36Sopenharmony_ci case MLXREG_LC_REG_RESET_CAUSE_OFFSET: 11362306a36Sopenharmony_ci case MLXREG_LC_REG_LED1_OFFSET: 11462306a36Sopenharmony_ci case MLXREG_LC_REG_GP0_OFFSET: 11562306a36Sopenharmony_ci case MLXREG_LC_REG_FIELD_UPGRADE: 11662306a36Sopenharmony_ci case MLXREG_LC_CHANNEL_I2C_REG: 11762306a36Sopenharmony_ci case MLXREG_LC_REG_CPLD1_MVER_OFFSET: 11862306a36Sopenharmony_ci case MLXREG_LC_REG_FPGA1_MVER_OFFSET: 11962306a36Sopenharmony_ci case MLXREG_LC_REG_MAX_POWER_OFFSET: 12062306a36Sopenharmony_ci case MLXREG_LC_REG_CONFIG_OFFSET: 12162306a36Sopenharmony_ci return true; 12262306a36Sopenharmony_ci } 12362306a36Sopenharmony_ci return false; 12462306a36Sopenharmony_ci} 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_cistatic bool mlxreg_lc_volatile_reg(struct device *dev, unsigned int reg) 12762306a36Sopenharmony_ci{ 12862306a36Sopenharmony_ci switch (reg) { 12962306a36Sopenharmony_ci case MLXREG_LC_REG_CPLD1_VER_OFFSET: 13062306a36Sopenharmony_ci case MLXREG_LC_REG_FPGA1_VER_OFFSET: 13162306a36Sopenharmony_ci case MLXREG_LC_REG_CPLD1_PN_OFFSET: 13262306a36Sopenharmony_ci case MLXREG_LC_REG_FPGA1_PN_OFFSET: 13362306a36Sopenharmony_ci case MLXREG_LC_REG_RESET_CAUSE_OFFSET: 13462306a36Sopenharmony_ci case MLXREG_LC_REG_LED1_OFFSET: 13562306a36Sopenharmony_ci case MLXREG_LC_REG_GP0_OFFSET: 13662306a36Sopenharmony_ci case MLXREG_LC_REG_FIELD_UPGRADE: 13762306a36Sopenharmony_ci case MLXREG_LC_CHANNEL_I2C_REG: 13862306a36Sopenharmony_ci case MLXREG_LC_REG_CPLD1_MVER_OFFSET: 13962306a36Sopenharmony_ci case MLXREG_LC_REG_FPGA1_MVER_OFFSET: 14062306a36Sopenharmony_ci case MLXREG_LC_REG_MAX_POWER_OFFSET: 14162306a36Sopenharmony_ci case MLXREG_LC_REG_CONFIG_OFFSET: 14262306a36Sopenharmony_ci return true; 14362306a36Sopenharmony_ci } 14462306a36Sopenharmony_ci return false; 14562306a36Sopenharmony_ci} 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_cistatic const struct reg_default mlxreg_lc_regmap_default[] = { 14862306a36Sopenharmony_ci { MLXREG_LC_CHANNEL_I2C_REG, 0x00 }, 14962306a36Sopenharmony_ci}; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci/* Configuration for the register map of a device with 2 bytes address space. */ 15262306a36Sopenharmony_cistatic const struct regmap_config mlxreg_lc_regmap_conf = { 15362306a36Sopenharmony_ci .reg_bits = 16, 15462306a36Sopenharmony_ci .val_bits = 8, 15562306a36Sopenharmony_ci .max_register = MLXREG_LC_REG_MAX, 15662306a36Sopenharmony_ci .cache_type = REGCACHE_FLAT, 15762306a36Sopenharmony_ci .writeable_reg = mlxreg_lc_writeable_reg, 15862306a36Sopenharmony_ci .readable_reg = mlxreg_lc_readable_reg, 15962306a36Sopenharmony_ci .volatile_reg = mlxreg_lc_volatile_reg, 16062306a36Sopenharmony_ci .reg_defaults = mlxreg_lc_regmap_default, 16162306a36Sopenharmony_ci .num_reg_defaults = ARRAY_SIZE(mlxreg_lc_regmap_default), 16262306a36Sopenharmony_ci}; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci/* Default channels vector. 16562306a36Sopenharmony_ci * It contains only the channels, which physically connected to the devices, 16662306a36Sopenharmony_ci * empty channels are skipped. 16762306a36Sopenharmony_ci */ 16862306a36Sopenharmony_cistatic int mlxreg_lc_chan[] = { 16962306a36Sopenharmony_ci 0x04, 0x05, 0x06, 0x07, 0x08, 0x10, 0x20, 0x21, 0x22, 0x23, 0x40, 0x41, 17062306a36Sopenharmony_ci 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 17162306a36Sopenharmony_ci 0x4e, 0x4f 17262306a36Sopenharmony_ci}; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci/* Defaul mux configuration. */ 17562306a36Sopenharmony_cistatic struct mlxcpld_mux_plat_data mlxreg_lc_mux_data[] = { 17662306a36Sopenharmony_ci { 17762306a36Sopenharmony_ci .chan_ids = mlxreg_lc_chan, 17862306a36Sopenharmony_ci .num_adaps = ARRAY_SIZE(mlxreg_lc_chan), 17962306a36Sopenharmony_ci .sel_reg_addr = MLXREG_LC_CHANNEL_I2C_REG, 18062306a36Sopenharmony_ci .reg_size = 2, 18162306a36Sopenharmony_ci }, 18262306a36Sopenharmony_ci}; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci/* Defaul mux board info. */ 18562306a36Sopenharmony_cistatic struct i2c_board_info mlxreg_lc_mux_brdinfo = { 18662306a36Sopenharmony_ci I2C_BOARD_INFO("i2c-mux-mlxcpld", 0x32), 18762306a36Sopenharmony_ci}; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci/* Line card default auxiliary power static devices. */ 19062306a36Sopenharmony_cistatic struct i2c_board_info mlxreg_lc_aux_pwr_devices[] = { 19162306a36Sopenharmony_ci { 19262306a36Sopenharmony_ci I2C_BOARD_INFO("24c32", 0x51), 19362306a36Sopenharmony_ci }, 19462306a36Sopenharmony_ci { 19562306a36Sopenharmony_ci I2C_BOARD_INFO("24c32", 0x51), 19662306a36Sopenharmony_ci }, 19762306a36Sopenharmony_ci}; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci/* Line card default auxiliary power board info. */ 20062306a36Sopenharmony_cistatic struct mlxreg_hotplug_device mlxreg_lc_aux_pwr_brdinfo[] = { 20162306a36Sopenharmony_ci { 20262306a36Sopenharmony_ci .brdinfo = &mlxreg_lc_aux_pwr_devices[0], 20362306a36Sopenharmony_ci .nr = 3, 20462306a36Sopenharmony_ci }, 20562306a36Sopenharmony_ci { 20662306a36Sopenharmony_ci .brdinfo = &mlxreg_lc_aux_pwr_devices[1], 20762306a36Sopenharmony_ci .nr = 4, 20862306a36Sopenharmony_ci }, 20962306a36Sopenharmony_ci}; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci/* Line card default main power static devices. */ 21262306a36Sopenharmony_cistatic struct i2c_board_info mlxreg_lc_main_pwr_devices[] = { 21362306a36Sopenharmony_ci { 21462306a36Sopenharmony_ci I2C_BOARD_INFO("mp2975", 0x62), 21562306a36Sopenharmony_ci }, 21662306a36Sopenharmony_ci { 21762306a36Sopenharmony_ci I2C_BOARD_INFO("mp2975", 0x64), 21862306a36Sopenharmony_ci }, 21962306a36Sopenharmony_ci { 22062306a36Sopenharmony_ci I2C_BOARD_INFO("max11603", 0x6d), 22162306a36Sopenharmony_ci }, 22262306a36Sopenharmony_ci { 22362306a36Sopenharmony_ci I2C_BOARD_INFO("lm25066", 0x15), 22462306a36Sopenharmony_ci }, 22562306a36Sopenharmony_ci}; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci/* Line card default main power board info. */ 22862306a36Sopenharmony_cistatic struct mlxreg_hotplug_device mlxreg_lc_main_pwr_brdinfo[] = { 22962306a36Sopenharmony_ci { 23062306a36Sopenharmony_ci .brdinfo = &mlxreg_lc_main_pwr_devices[0], 23162306a36Sopenharmony_ci .nr = 0, 23262306a36Sopenharmony_ci }, 23362306a36Sopenharmony_ci { 23462306a36Sopenharmony_ci .brdinfo = &mlxreg_lc_main_pwr_devices[1], 23562306a36Sopenharmony_ci .nr = 0, 23662306a36Sopenharmony_ci }, 23762306a36Sopenharmony_ci { 23862306a36Sopenharmony_ci .brdinfo = &mlxreg_lc_main_pwr_devices[2], 23962306a36Sopenharmony_ci .nr = 1, 24062306a36Sopenharmony_ci }, 24162306a36Sopenharmony_ci { 24262306a36Sopenharmony_ci .brdinfo = &mlxreg_lc_main_pwr_devices[3], 24362306a36Sopenharmony_ci .nr = 2, 24462306a36Sopenharmony_ci }, 24562306a36Sopenharmony_ci}; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci/* LED default data. */ 24862306a36Sopenharmony_cistatic struct mlxreg_core_data mlxreg_lc_led_data[] = { 24962306a36Sopenharmony_ci { 25062306a36Sopenharmony_ci .label = "status:green", 25162306a36Sopenharmony_ci .reg = MLXREG_LC_REG_LED1_OFFSET, 25262306a36Sopenharmony_ci .mask = GENMASK(7, 4), 25362306a36Sopenharmony_ci }, 25462306a36Sopenharmony_ci { 25562306a36Sopenharmony_ci .label = "status:orange", 25662306a36Sopenharmony_ci .reg = MLXREG_LC_REG_LED1_OFFSET, 25762306a36Sopenharmony_ci .mask = GENMASK(7, 4), 25862306a36Sopenharmony_ci }, 25962306a36Sopenharmony_ci}; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_cistatic struct mlxreg_core_platform_data mlxreg_lc_led = { 26262306a36Sopenharmony_ci .identity = "pci", 26362306a36Sopenharmony_ci .data = mlxreg_lc_led_data, 26462306a36Sopenharmony_ci .counter = ARRAY_SIZE(mlxreg_lc_led_data), 26562306a36Sopenharmony_ci}; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci/* Default register access data. */ 26862306a36Sopenharmony_cistatic struct mlxreg_core_data mlxreg_lc_io_data[] = { 26962306a36Sopenharmony_ci { 27062306a36Sopenharmony_ci .label = "cpld1_version", 27162306a36Sopenharmony_ci .reg = MLXREG_LC_REG_CPLD1_VER_OFFSET, 27262306a36Sopenharmony_ci .bit = GENMASK(7, 0), 27362306a36Sopenharmony_ci .mode = 0444, 27462306a36Sopenharmony_ci }, 27562306a36Sopenharmony_ci { 27662306a36Sopenharmony_ci .label = "fpga1_version", 27762306a36Sopenharmony_ci .reg = MLXREG_LC_REG_FPGA1_VER_OFFSET, 27862306a36Sopenharmony_ci .bit = GENMASK(7, 0), 27962306a36Sopenharmony_ci .mode = 0444, 28062306a36Sopenharmony_ci }, 28162306a36Sopenharmony_ci { 28262306a36Sopenharmony_ci .label = "cpld1_pn", 28362306a36Sopenharmony_ci .reg = MLXREG_LC_REG_CPLD1_PN_OFFSET, 28462306a36Sopenharmony_ci .bit = GENMASK(15, 0), 28562306a36Sopenharmony_ci .mode = 0444, 28662306a36Sopenharmony_ci .regnum = 2, 28762306a36Sopenharmony_ci }, 28862306a36Sopenharmony_ci { 28962306a36Sopenharmony_ci .label = "fpga1_pn", 29062306a36Sopenharmony_ci .reg = MLXREG_LC_REG_FPGA1_PN_OFFSET, 29162306a36Sopenharmony_ci .bit = GENMASK(15, 0), 29262306a36Sopenharmony_ci .mode = 0444, 29362306a36Sopenharmony_ci .regnum = 2, 29462306a36Sopenharmony_ci }, 29562306a36Sopenharmony_ci { 29662306a36Sopenharmony_ci .label = "cpld1_version_min", 29762306a36Sopenharmony_ci .reg = MLXREG_LC_REG_CPLD1_MVER_OFFSET, 29862306a36Sopenharmony_ci .bit = GENMASK(7, 0), 29962306a36Sopenharmony_ci .mode = 0444, 30062306a36Sopenharmony_ci }, 30162306a36Sopenharmony_ci { 30262306a36Sopenharmony_ci .label = "fpga1_version_min", 30362306a36Sopenharmony_ci .reg = MLXREG_LC_REG_FPGA1_MVER_OFFSET, 30462306a36Sopenharmony_ci .bit = GENMASK(7, 0), 30562306a36Sopenharmony_ci .mode = 0444, 30662306a36Sopenharmony_ci }, 30762306a36Sopenharmony_ci { 30862306a36Sopenharmony_ci .label = "reset_fpga_not_done", 30962306a36Sopenharmony_ci .reg = MLXREG_LC_REG_RESET_CAUSE_OFFSET, 31062306a36Sopenharmony_ci .mask = GENMASK(7, 0) & ~BIT(1), 31162306a36Sopenharmony_ci .mode = 0444, 31262306a36Sopenharmony_ci }, 31362306a36Sopenharmony_ci { 31462306a36Sopenharmony_ci .label = "reset_aux_pwr_or_ref", 31562306a36Sopenharmony_ci .reg = MLXREG_LC_REG_RESET_CAUSE_OFFSET, 31662306a36Sopenharmony_ci .mask = GENMASK(7, 0) & ~BIT(2), 31762306a36Sopenharmony_ci .mode = 0444, 31862306a36Sopenharmony_ci }, 31962306a36Sopenharmony_ci { 32062306a36Sopenharmony_ci .label = "reset_dc_dc_pwr_fail", 32162306a36Sopenharmony_ci .reg = MLXREG_LC_REG_RESET_CAUSE_OFFSET, 32262306a36Sopenharmony_ci .mask = GENMASK(7, 0) & ~BIT(3), 32362306a36Sopenharmony_ci .mode = 0444, 32462306a36Sopenharmony_ci }, 32562306a36Sopenharmony_ci { 32662306a36Sopenharmony_ci .label = "reset_from_chassis", 32762306a36Sopenharmony_ci .reg = MLXREG_LC_REG_RESET_CAUSE_OFFSET, 32862306a36Sopenharmony_ci .mask = GENMASK(7, 0) & ~BIT(4), 32962306a36Sopenharmony_ci .mode = 0444, 33062306a36Sopenharmony_ci }, 33162306a36Sopenharmony_ci { 33262306a36Sopenharmony_ci .label = "reset_pwr_off_from_chassis", 33362306a36Sopenharmony_ci .reg = MLXREG_LC_REG_RESET_CAUSE_OFFSET, 33462306a36Sopenharmony_ci .mask = GENMASK(7, 0) & ~BIT(5), 33562306a36Sopenharmony_ci .mode = 0444, 33662306a36Sopenharmony_ci }, 33762306a36Sopenharmony_ci { 33862306a36Sopenharmony_ci .label = "reset_line_card", 33962306a36Sopenharmony_ci .reg = MLXREG_LC_REG_RESET_CAUSE_OFFSET, 34062306a36Sopenharmony_ci .mask = GENMASK(7, 0) & ~BIT(6), 34162306a36Sopenharmony_ci .mode = 0444, 34262306a36Sopenharmony_ci }, 34362306a36Sopenharmony_ci { 34462306a36Sopenharmony_ci .label = "reset_line_card_pwr_en", 34562306a36Sopenharmony_ci .reg = MLXREG_LC_REG_RESET_CAUSE_OFFSET, 34662306a36Sopenharmony_ci .mask = GENMASK(7, 0) & ~BIT(7), 34762306a36Sopenharmony_ci .mode = 0444, 34862306a36Sopenharmony_ci }, 34962306a36Sopenharmony_ci { 35062306a36Sopenharmony_ci .label = "cpld_upgrade_en", 35162306a36Sopenharmony_ci .reg = MLXREG_LC_REG_FIELD_UPGRADE, 35262306a36Sopenharmony_ci .mask = GENMASK(7, 0) & ~BIT(0), 35362306a36Sopenharmony_ci .mode = 0644, 35462306a36Sopenharmony_ci .secured = 1, 35562306a36Sopenharmony_ci }, 35662306a36Sopenharmony_ci { 35762306a36Sopenharmony_ci .label = "fpga_upgrade_en", 35862306a36Sopenharmony_ci .reg = MLXREG_LC_REG_FIELD_UPGRADE, 35962306a36Sopenharmony_ci .mask = GENMASK(7, 0) & ~BIT(1), 36062306a36Sopenharmony_ci .mode = 0644, 36162306a36Sopenharmony_ci .secured = 1, 36262306a36Sopenharmony_ci }, 36362306a36Sopenharmony_ci { 36462306a36Sopenharmony_ci .label = "qsfp_pwr_en", 36562306a36Sopenharmony_ci .reg = MLXREG_LC_REG_GP0_OFFSET, 36662306a36Sopenharmony_ci .mask = GENMASK(7, 0) & ~BIT(0), 36762306a36Sopenharmony_ci .mode = 0644, 36862306a36Sopenharmony_ci }, 36962306a36Sopenharmony_ci { 37062306a36Sopenharmony_ci .label = "vpd_wp", 37162306a36Sopenharmony_ci .reg = MLXREG_LC_REG_GP0_OFFSET, 37262306a36Sopenharmony_ci .mask = GENMASK(7, 0) & ~BIT(3), 37362306a36Sopenharmony_ci .mode = 0644, 37462306a36Sopenharmony_ci .secured = 1, 37562306a36Sopenharmony_ci }, 37662306a36Sopenharmony_ci { 37762306a36Sopenharmony_ci .label = "agb_spi_burn_en", 37862306a36Sopenharmony_ci .reg = MLXREG_LC_REG_GP0_OFFSET, 37962306a36Sopenharmony_ci .mask = GENMASK(7, 0) & ~BIT(5), 38062306a36Sopenharmony_ci .mode = 0644, 38162306a36Sopenharmony_ci .secured = 1, 38262306a36Sopenharmony_ci }, 38362306a36Sopenharmony_ci { 38462306a36Sopenharmony_ci .label = "fpga_spi_burn_en", 38562306a36Sopenharmony_ci .reg = MLXREG_LC_REG_GP0_OFFSET, 38662306a36Sopenharmony_ci .mask = GENMASK(7, 0) & ~BIT(6), 38762306a36Sopenharmony_ci .mode = 0644, 38862306a36Sopenharmony_ci .secured = 1, 38962306a36Sopenharmony_ci }, 39062306a36Sopenharmony_ci { 39162306a36Sopenharmony_ci .label = "max_power", 39262306a36Sopenharmony_ci .reg = MLXREG_LC_REG_MAX_POWER_OFFSET, 39362306a36Sopenharmony_ci .bit = GENMASK(15, 0), 39462306a36Sopenharmony_ci .mode = 0444, 39562306a36Sopenharmony_ci .regnum = 2, 39662306a36Sopenharmony_ci }, 39762306a36Sopenharmony_ci { 39862306a36Sopenharmony_ci .label = "config", 39962306a36Sopenharmony_ci .reg = MLXREG_LC_REG_CONFIG_OFFSET, 40062306a36Sopenharmony_ci .bit = GENMASK(15, 0), 40162306a36Sopenharmony_ci .mode = 0444, 40262306a36Sopenharmony_ci .regnum = 2, 40362306a36Sopenharmony_ci }, 40462306a36Sopenharmony_ci}; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_cistatic struct mlxreg_core_platform_data mlxreg_lc_regs_io = { 40762306a36Sopenharmony_ci .data = mlxreg_lc_io_data, 40862306a36Sopenharmony_ci .counter = ARRAY_SIZE(mlxreg_lc_io_data), 40962306a36Sopenharmony_ci}; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_cistatic int 41262306a36Sopenharmony_cimlxreg_lc_create_static_devices(struct mlxreg_lc *mlxreg_lc, struct mlxreg_hotplug_device *devs, 41362306a36Sopenharmony_ci int size) 41462306a36Sopenharmony_ci{ 41562306a36Sopenharmony_ci struct mlxreg_hotplug_device *dev = devs; 41662306a36Sopenharmony_ci int i, ret; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci /* Create static I2C device feeding by auxiliary or main power. */ 41962306a36Sopenharmony_ci for (i = 0; i < size; i++, dev++) { 42062306a36Sopenharmony_ci dev->client = i2c_new_client_device(dev->adapter, dev->brdinfo); 42162306a36Sopenharmony_ci if (IS_ERR(dev->client)) { 42262306a36Sopenharmony_ci dev_err(mlxreg_lc->dev, "Failed to create client %s at bus %d at addr 0x%02x\n", 42362306a36Sopenharmony_ci dev->brdinfo->type, dev->nr, dev->brdinfo->addr); 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci dev->adapter = NULL; 42662306a36Sopenharmony_ci ret = PTR_ERR(dev->client); 42762306a36Sopenharmony_ci goto fail_create_static_devices; 42862306a36Sopenharmony_ci } 42962306a36Sopenharmony_ci } 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci return 0; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_cifail_create_static_devices: 43462306a36Sopenharmony_ci while (--i >= 0) { 43562306a36Sopenharmony_ci dev = devs + i; 43662306a36Sopenharmony_ci i2c_unregister_device(dev->client); 43762306a36Sopenharmony_ci dev->client = NULL; 43862306a36Sopenharmony_ci } 43962306a36Sopenharmony_ci return ret; 44062306a36Sopenharmony_ci} 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_cistatic void 44362306a36Sopenharmony_cimlxreg_lc_destroy_static_devices(struct mlxreg_lc *mlxreg_lc, struct mlxreg_hotplug_device *devs, 44462306a36Sopenharmony_ci int size) 44562306a36Sopenharmony_ci{ 44662306a36Sopenharmony_ci struct mlxreg_hotplug_device *dev = devs; 44762306a36Sopenharmony_ci int i; 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci /* Destroy static I2C device feeding by auxiliary or main power. */ 45062306a36Sopenharmony_ci for (i = 0; i < size; i++, dev++) { 45162306a36Sopenharmony_ci if (dev->client) { 45262306a36Sopenharmony_ci i2c_unregister_device(dev->client); 45362306a36Sopenharmony_ci dev->client = NULL; 45462306a36Sopenharmony_ci } 45562306a36Sopenharmony_ci } 45662306a36Sopenharmony_ci} 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_cistatic int mlxreg_lc_power_on_off(struct mlxreg_lc *mlxreg_lc, u8 action) 45962306a36Sopenharmony_ci{ 46062306a36Sopenharmony_ci u32 regval; 46162306a36Sopenharmony_ci int err; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci err = regmap_read(mlxreg_lc->par_regmap, mlxreg_lc->data->reg_pwr, ®val); 46462306a36Sopenharmony_ci if (err) 46562306a36Sopenharmony_ci goto regmap_read_fail; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci if (action) 46862306a36Sopenharmony_ci regval |= BIT(mlxreg_lc->data->slot - 1); 46962306a36Sopenharmony_ci else 47062306a36Sopenharmony_ci regval &= ~BIT(mlxreg_lc->data->slot - 1); 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci err = regmap_write(mlxreg_lc->par_regmap, mlxreg_lc->data->reg_pwr, regval); 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ciregmap_read_fail: 47562306a36Sopenharmony_ci return err; 47662306a36Sopenharmony_ci} 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_cistatic int mlxreg_lc_enable_disable(struct mlxreg_lc *mlxreg_lc, bool action) 47962306a36Sopenharmony_ci{ 48062306a36Sopenharmony_ci u32 regval; 48162306a36Sopenharmony_ci int err; 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci /* 48462306a36Sopenharmony_ci * Hardware holds the line card after powering on in the disabled state. Holding line card 48562306a36Sopenharmony_ci * in disabled state protects access to the line components, like FPGA and gearboxes. 48662306a36Sopenharmony_ci * Line card should be enabled in order to get it in operational state. Line card could be 48762306a36Sopenharmony_ci * disabled for moving it to non-operational state. Enabling line card does not affect the 48862306a36Sopenharmony_ci * line card which is already has been enabled. Disabling does not affect the disabled line 48962306a36Sopenharmony_ci * card. 49062306a36Sopenharmony_ci */ 49162306a36Sopenharmony_ci err = regmap_read(mlxreg_lc->par_regmap, mlxreg_lc->data->reg_ena, ®val); 49262306a36Sopenharmony_ci if (err) 49362306a36Sopenharmony_ci goto regmap_read_fail; 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci if (action) 49662306a36Sopenharmony_ci regval |= BIT(mlxreg_lc->data->slot - 1); 49762306a36Sopenharmony_ci else 49862306a36Sopenharmony_ci regval &= ~BIT(mlxreg_lc->data->slot - 1); 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci err = regmap_write(mlxreg_lc->par_regmap, mlxreg_lc->data->reg_ena, regval); 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ciregmap_read_fail: 50362306a36Sopenharmony_ci return err; 50462306a36Sopenharmony_ci} 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_cistatic int 50762306a36Sopenharmony_cimlxreg_lc_sn4800_c16_config_init(struct mlxreg_lc *mlxreg_lc, void *regmap, 50862306a36Sopenharmony_ci struct mlxreg_core_data *data) 50962306a36Sopenharmony_ci{ 51062306a36Sopenharmony_ci struct device *dev = &data->hpdev.client->dev; 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci /* Set line card configuration according to the type. */ 51362306a36Sopenharmony_ci mlxreg_lc->mux_data = mlxreg_lc_mux_data; 51462306a36Sopenharmony_ci mlxreg_lc->io_data = &mlxreg_lc_regs_io; 51562306a36Sopenharmony_ci mlxreg_lc->led_data = &mlxreg_lc_led; 51662306a36Sopenharmony_ci mlxreg_lc->mux_brdinfo = &mlxreg_lc_mux_brdinfo; 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci mlxreg_lc->aux_devs = devm_kmemdup(dev, mlxreg_lc_aux_pwr_brdinfo, 51962306a36Sopenharmony_ci sizeof(mlxreg_lc_aux_pwr_brdinfo), GFP_KERNEL); 52062306a36Sopenharmony_ci if (!mlxreg_lc->aux_devs) 52162306a36Sopenharmony_ci return -ENOMEM; 52262306a36Sopenharmony_ci mlxreg_lc->aux_devs_num = ARRAY_SIZE(mlxreg_lc_aux_pwr_brdinfo); 52362306a36Sopenharmony_ci mlxreg_lc->main_devs = devm_kmemdup(dev, mlxreg_lc_main_pwr_brdinfo, 52462306a36Sopenharmony_ci sizeof(mlxreg_lc_main_pwr_brdinfo), GFP_KERNEL); 52562306a36Sopenharmony_ci if (!mlxreg_lc->main_devs) 52662306a36Sopenharmony_ci return -ENOMEM; 52762306a36Sopenharmony_ci mlxreg_lc->main_devs_num = ARRAY_SIZE(mlxreg_lc_main_pwr_brdinfo); 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci return 0; 53062306a36Sopenharmony_ci} 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_cistatic void 53362306a36Sopenharmony_cimlxreg_lc_state_update(struct mlxreg_lc *mlxreg_lc, enum mlxreg_lc_state state, u8 action) 53462306a36Sopenharmony_ci{ 53562306a36Sopenharmony_ci if (action) 53662306a36Sopenharmony_ci mlxreg_lc->state |= state; 53762306a36Sopenharmony_ci else 53862306a36Sopenharmony_ci mlxreg_lc->state &= ~state; 53962306a36Sopenharmony_ci} 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_cistatic void 54262306a36Sopenharmony_cimlxreg_lc_state_update_locked(struct mlxreg_lc *mlxreg_lc, enum mlxreg_lc_state state, u8 action) 54362306a36Sopenharmony_ci{ 54462306a36Sopenharmony_ci mutex_lock(&mlxreg_lc->lock); 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci if (action) 54762306a36Sopenharmony_ci mlxreg_lc->state |= state; 54862306a36Sopenharmony_ci else 54962306a36Sopenharmony_ci mlxreg_lc->state &= ~state; 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci mutex_unlock(&mlxreg_lc->lock); 55262306a36Sopenharmony_ci} 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci/* 55562306a36Sopenharmony_ci * Callback is to be called from mlxreg-hotplug driver to notify about line card about received 55662306a36Sopenharmony_ci * event. 55762306a36Sopenharmony_ci */ 55862306a36Sopenharmony_cistatic int mlxreg_lc_event_handler(void *handle, enum mlxreg_hotplug_kind kind, u8 action) 55962306a36Sopenharmony_ci{ 56062306a36Sopenharmony_ci struct mlxreg_lc *mlxreg_lc = handle; 56162306a36Sopenharmony_ci int err = 0; 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci dev_info(mlxreg_lc->dev, "linecard#%d state %d event kind %d action %d\n", 56462306a36Sopenharmony_ci mlxreg_lc->data->slot, mlxreg_lc->state, kind, action); 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci mutex_lock(&mlxreg_lc->lock); 56762306a36Sopenharmony_ci if (!(mlxreg_lc->state & MLXREG_LC_INITIALIZED)) 56862306a36Sopenharmony_ci goto mlxreg_lc_non_initialzed_exit; 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci switch (kind) { 57162306a36Sopenharmony_ci case MLXREG_HOTPLUG_LC_SYNCED: 57262306a36Sopenharmony_ci /* 57362306a36Sopenharmony_ci * Synchronization event - hardware and firmware are synchronized. Power on/off 57462306a36Sopenharmony_ci * line card - to allow/disallow main power source. 57562306a36Sopenharmony_ci */ 57662306a36Sopenharmony_ci mlxreg_lc_state_update(mlxreg_lc, MLXREG_LC_SYNCED, action); 57762306a36Sopenharmony_ci /* Power line card if it is not powered yet. */ 57862306a36Sopenharmony_ci if (!(mlxreg_lc->state & MLXREG_LC_POWERED) && action) { 57962306a36Sopenharmony_ci err = mlxreg_lc_power_on_off(mlxreg_lc, 1); 58062306a36Sopenharmony_ci if (err) 58162306a36Sopenharmony_ci goto mlxreg_lc_power_on_off_fail; 58262306a36Sopenharmony_ci } 58362306a36Sopenharmony_ci /* In case line card is configured - enable it. */ 58462306a36Sopenharmony_ci if (mlxreg_lc->state & MLXREG_LC_CONFIGURED && action) 58562306a36Sopenharmony_ci err = mlxreg_lc_enable_disable(mlxreg_lc, 1); 58662306a36Sopenharmony_ci break; 58762306a36Sopenharmony_ci case MLXREG_HOTPLUG_LC_POWERED: 58862306a36Sopenharmony_ci /* Power event - attach or de-attach line card device feeding by the main power. */ 58962306a36Sopenharmony_ci if (action) { 59062306a36Sopenharmony_ci /* Do not create devices, if line card is already powered. */ 59162306a36Sopenharmony_ci if (mlxreg_lc->state & MLXREG_LC_POWERED) { 59262306a36Sopenharmony_ci /* In case line card is configured - enable it. */ 59362306a36Sopenharmony_ci if (mlxreg_lc->state & MLXREG_LC_CONFIGURED) 59462306a36Sopenharmony_ci err = mlxreg_lc_enable_disable(mlxreg_lc, 1); 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci goto mlxreg_lc_enable_disable_exit; 59762306a36Sopenharmony_ci } 59862306a36Sopenharmony_ci err = mlxreg_lc_create_static_devices(mlxreg_lc, mlxreg_lc->main_devs, 59962306a36Sopenharmony_ci mlxreg_lc->main_devs_num); 60062306a36Sopenharmony_ci if (err) 60162306a36Sopenharmony_ci goto mlxreg_lc_create_static_devices_fail; 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci /* In case line card is already in ready state - enable it. */ 60462306a36Sopenharmony_ci if (mlxreg_lc->state & MLXREG_LC_CONFIGURED) 60562306a36Sopenharmony_ci err = mlxreg_lc_enable_disable(mlxreg_lc, 1); 60662306a36Sopenharmony_ci } else { 60762306a36Sopenharmony_ci mlxreg_lc_destroy_static_devices(mlxreg_lc, mlxreg_lc->main_devs, 60862306a36Sopenharmony_ci mlxreg_lc->main_devs_num); 60962306a36Sopenharmony_ci } 61062306a36Sopenharmony_ci mlxreg_lc_state_update(mlxreg_lc, MLXREG_LC_POWERED, action); 61162306a36Sopenharmony_ci break; 61262306a36Sopenharmony_ci case MLXREG_HOTPLUG_LC_READY: 61362306a36Sopenharmony_ci /* 61462306a36Sopenharmony_ci * Ready event – enable line card by releasing it from reset or disable it by put 61562306a36Sopenharmony_ci * to reset state. 61662306a36Sopenharmony_ci */ 61762306a36Sopenharmony_ci err = mlxreg_lc_enable_disable(mlxreg_lc, !!action); 61862306a36Sopenharmony_ci break; 61962306a36Sopenharmony_ci case MLXREG_HOTPLUG_LC_THERMAL: 62062306a36Sopenharmony_ci /* Thermal shutdown event – power off line card. */ 62162306a36Sopenharmony_ci if (action) 62262306a36Sopenharmony_ci err = mlxreg_lc_power_on_off(mlxreg_lc, 0); 62362306a36Sopenharmony_ci break; 62462306a36Sopenharmony_ci default: 62562306a36Sopenharmony_ci break; 62662306a36Sopenharmony_ci } 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_cimlxreg_lc_enable_disable_exit: 62962306a36Sopenharmony_cimlxreg_lc_power_on_off_fail: 63062306a36Sopenharmony_cimlxreg_lc_create_static_devices_fail: 63162306a36Sopenharmony_cimlxreg_lc_non_initialzed_exit: 63262306a36Sopenharmony_ci mutex_unlock(&mlxreg_lc->lock); 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci return err; 63562306a36Sopenharmony_ci} 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci/* 63862306a36Sopenharmony_ci * Callback is to be called from i2c-mux-mlxcpld driver to indicate that all adapter devices has 63962306a36Sopenharmony_ci * been created. 64062306a36Sopenharmony_ci */ 64162306a36Sopenharmony_cistatic int mlxreg_lc_completion_notify(void *handle, struct i2c_adapter *parent, 64262306a36Sopenharmony_ci struct i2c_adapter *adapters[]) 64362306a36Sopenharmony_ci{ 64462306a36Sopenharmony_ci struct mlxreg_hotplug_device *main_dev, *aux_dev; 64562306a36Sopenharmony_ci struct mlxreg_lc *mlxreg_lc = handle; 64662306a36Sopenharmony_ci u32 regval; 64762306a36Sopenharmony_ci int i, err; 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci /* Update I2C devices feeding by auxiliary power. */ 65062306a36Sopenharmony_ci aux_dev = mlxreg_lc->aux_devs; 65162306a36Sopenharmony_ci for (i = 0; i < mlxreg_lc->aux_devs_num; i++, aux_dev++) { 65262306a36Sopenharmony_ci aux_dev->adapter = adapters[aux_dev->nr]; 65362306a36Sopenharmony_ci aux_dev->nr = adapters[aux_dev->nr]->nr; 65462306a36Sopenharmony_ci } 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci err = mlxreg_lc_create_static_devices(mlxreg_lc, mlxreg_lc->aux_devs, 65762306a36Sopenharmony_ci mlxreg_lc->aux_devs_num); 65862306a36Sopenharmony_ci if (err) 65962306a36Sopenharmony_ci return err; 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci /* Update I2C devices feeding by main power. */ 66262306a36Sopenharmony_ci main_dev = mlxreg_lc->main_devs; 66362306a36Sopenharmony_ci for (i = 0; i < mlxreg_lc->main_devs_num; i++, main_dev++) { 66462306a36Sopenharmony_ci main_dev->adapter = adapters[main_dev->nr]; 66562306a36Sopenharmony_ci main_dev->nr = adapters[main_dev->nr]->nr; 66662306a36Sopenharmony_ci } 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci /* Verify if line card is powered. */ 66962306a36Sopenharmony_ci err = regmap_read(mlxreg_lc->par_regmap, mlxreg_lc->data->reg_pwr, ®val); 67062306a36Sopenharmony_ci if (err) 67162306a36Sopenharmony_ci goto mlxreg_lc_regmap_read_power_fail; 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci if (regval & mlxreg_lc->data->mask) { 67462306a36Sopenharmony_ci err = mlxreg_lc_create_static_devices(mlxreg_lc, mlxreg_lc->main_devs, 67562306a36Sopenharmony_ci mlxreg_lc->main_devs_num); 67662306a36Sopenharmony_ci if (err) 67762306a36Sopenharmony_ci goto mlxreg_lc_create_static_devices_failed; 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci mlxreg_lc_state_update_locked(mlxreg_lc, MLXREG_LC_POWERED, 1); 68062306a36Sopenharmony_ci } 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci /* Verify if line card is synchronized. */ 68362306a36Sopenharmony_ci err = regmap_read(mlxreg_lc->par_regmap, mlxreg_lc->data->reg_sync, ®val); 68462306a36Sopenharmony_ci if (err) 68562306a36Sopenharmony_ci goto mlxreg_lc_regmap_read_sync_fail; 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci /* Power on line card if necessary. */ 68862306a36Sopenharmony_ci if (regval & mlxreg_lc->data->mask) { 68962306a36Sopenharmony_ci mlxreg_lc->state |= MLXREG_LC_SYNCED; 69062306a36Sopenharmony_ci mlxreg_lc_state_update_locked(mlxreg_lc, MLXREG_LC_SYNCED, 1); 69162306a36Sopenharmony_ci if (mlxreg_lc->state & ~MLXREG_LC_POWERED) { 69262306a36Sopenharmony_ci err = mlxreg_lc_power_on_off(mlxreg_lc, 1); 69362306a36Sopenharmony_ci if (err) 69462306a36Sopenharmony_ci goto mlxreg_lc_regmap_power_on_off_fail; 69562306a36Sopenharmony_ci } 69662306a36Sopenharmony_ci } 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci mlxreg_lc_state_update_locked(mlxreg_lc, MLXREG_LC_INITIALIZED, 1); 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci return 0; 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_cimlxreg_lc_regmap_power_on_off_fail: 70362306a36Sopenharmony_cimlxreg_lc_regmap_read_sync_fail: 70462306a36Sopenharmony_ci if (mlxreg_lc->state & MLXREG_LC_POWERED) 70562306a36Sopenharmony_ci mlxreg_lc_destroy_static_devices(mlxreg_lc, mlxreg_lc->main_devs, 70662306a36Sopenharmony_ci mlxreg_lc->main_devs_num); 70762306a36Sopenharmony_cimlxreg_lc_create_static_devices_failed: 70862306a36Sopenharmony_ci mlxreg_lc_destroy_static_devices(mlxreg_lc, mlxreg_lc->aux_devs, mlxreg_lc->aux_devs_num); 70962306a36Sopenharmony_cimlxreg_lc_regmap_read_power_fail: 71062306a36Sopenharmony_ci return err; 71162306a36Sopenharmony_ci} 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_cistatic int 71462306a36Sopenharmony_cimlxreg_lc_config_init(struct mlxreg_lc *mlxreg_lc, void *regmap, 71562306a36Sopenharmony_ci struct mlxreg_core_data *data) 71662306a36Sopenharmony_ci{ 71762306a36Sopenharmony_ci struct device *dev = &data->hpdev.client->dev; 71862306a36Sopenharmony_ci int lsb, err; 71962306a36Sopenharmony_ci u32 regval; 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci /* Validate line card type. */ 72262306a36Sopenharmony_ci err = regmap_read(regmap, MLXREG_LC_REG_CONFIG_OFFSET, &lsb); 72362306a36Sopenharmony_ci err = (!err) ? regmap_read(regmap, MLXREG_LC_REG_CONFIG_OFFSET, ®val) : err; 72462306a36Sopenharmony_ci if (err) 72562306a36Sopenharmony_ci return err; 72662306a36Sopenharmony_ci regval = (regval & GENMASK(7, 0)) << 8 | (lsb & GENMASK(7, 0)); 72762306a36Sopenharmony_ci switch (regval) { 72862306a36Sopenharmony_ci case MLXREG_LC_SN4800_C16: 72962306a36Sopenharmony_ci err = mlxreg_lc_sn4800_c16_config_init(mlxreg_lc, regmap, data); 73062306a36Sopenharmony_ci if (err) { 73162306a36Sopenharmony_ci dev_err(dev, "Failed to config client %s at bus %d at addr 0x%02x\n", 73262306a36Sopenharmony_ci data->hpdev.brdinfo->type, data->hpdev.nr, 73362306a36Sopenharmony_ci data->hpdev.brdinfo->addr); 73462306a36Sopenharmony_ci return err; 73562306a36Sopenharmony_ci } 73662306a36Sopenharmony_ci break; 73762306a36Sopenharmony_ci default: 73862306a36Sopenharmony_ci return -ENODEV; 73962306a36Sopenharmony_ci } 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci /* Create mux infrastructure. */ 74262306a36Sopenharmony_ci mlxreg_lc->mux_data->handle = mlxreg_lc; 74362306a36Sopenharmony_ci mlxreg_lc->mux_data->completion_notify = mlxreg_lc_completion_notify; 74462306a36Sopenharmony_ci mlxreg_lc->mux_brdinfo->platform_data = mlxreg_lc->mux_data; 74562306a36Sopenharmony_ci mlxreg_lc->mux = platform_device_register_resndata(dev, "i2c-mux-mlxcpld", data->hpdev.nr, 74662306a36Sopenharmony_ci NULL, 0, mlxreg_lc->mux_data, 74762306a36Sopenharmony_ci sizeof(*mlxreg_lc->mux_data)); 74862306a36Sopenharmony_ci if (IS_ERR(mlxreg_lc->mux)) { 74962306a36Sopenharmony_ci dev_err(dev, "Failed to create mux infra for client %s at bus %d at addr 0x%02x\n", 75062306a36Sopenharmony_ci data->hpdev.brdinfo->type, data->hpdev.nr, data->hpdev.brdinfo->addr); 75162306a36Sopenharmony_ci return PTR_ERR(mlxreg_lc->mux); 75262306a36Sopenharmony_ci } 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci /* Register IO access driver. */ 75562306a36Sopenharmony_ci if (mlxreg_lc->io_data) { 75662306a36Sopenharmony_ci mlxreg_lc->io_data->regmap = regmap; 75762306a36Sopenharmony_ci mlxreg_lc->io_regs = 75862306a36Sopenharmony_ci platform_device_register_resndata(dev, "mlxreg-io", data->hpdev.nr, NULL, 0, 75962306a36Sopenharmony_ci mlxreg_lc->io_data, sizeof(*mlxreg_lc->io_data)); 76062306a36Sopenharmony_ci if (IS_ERR(mlxreg_lc->io_regs)) { 76162306a36Sopenharmony_ci dev_err(dev, "Failed to create regio for client %s at bus %d at addr 0x%02x\n", 76262306a36Sopenharmony_ci data->hpdev.brdinfo->type, data->hpdev.nr, 76362306a36Sopenharmony_ci data->hpdev.brdinfo->addr); 76462306a36Sopenharmony_ci err = PTR_ERR(mlxreg_lc->io_regs); 76562306a36Sopenharmony_ci goto fail_register_io; 76662306a36Sopenharmony_ci } 76762306a36Sopenharmony_ci } 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci /* Register LED driver. */ 77062306a36Sopenharmony_ci if (mlxreg_lc->led_data) { 77162306a36Sopenharmony_ci mlxreg_lc->led_data->regmap = regmap; 77262306a36Sopenharmony_ci mlxreg_lc->led = 77362306a36Sopenharmony_ci platform_device_register_resndata(dev, "leds-mlxreg", data->hpdev.nr, NULL, 0, 77462306a36Sopenharmony_ci mlxreg_lc->led_data, 77562306a36Sopenharmony_ci sizeof(*mlxreg_lc->led_data)); 77662306a36Sopenharmony_ci if (IS_ERR(mlxreg_lc->led)) { 77762306a36Sopenharmony_ci dev_err(dev, "Failed to create LED objects for client %s at bus %d at addr 0x%02x\n", 77862306a36Sopenharmony_ci data->hpdev.brdinfo->type, data->hpdev.nr, 77962306a36Sopenharmony_ci data->hpdev.brdinfo->addr); 78062306a36Sopenharmony_ci err = PTR_ERR(mlxreg_lc->led); 78162306a36Sopenharmony_ci goto fail_register_led; 78262306a36Sopenharmony_ci } 78362306a36Sopenharmony_ci } 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci return 0; 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_cifail_register_led: 78862306a36Sopenharmony_ci if (mlxreg_lc->io_regs) 78962306a36Sopenharmony_ci platform_device_unregister(mlxreg_lc->io_regs); 79062306a36Sopenharmony_cifail_register_io: 79162306a36Sopenharmony_ci if (mlxreg_lc->mux) 79262306a36Sopenharmony_ci platform_device_unregister(mlxreg_lc->mux); 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci return err; 79562306a36Sopenharmony_ci} 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_cistatic void mlxreg_lc_config_exit(struct mlxreg_lc *mlxreg_lc) 79862306a36Sopenharmony_ci{ 79962306a36Sopenharmony_ci /* Unregister LED driver. */ 80062306a36Sopenharmony_ci if (mlxreg_lc->led) 80162306a36Sopenharmony_ci platform_device_unregister(mlxreg_lc->led); 80262306a36Sopenharmony_ci /* Unregister IO access driver. */ 80362306a36Sopenharmony_ci if (mlxreg_lc->io_regs) 80462306a36Sopenharmony_ci platform_device_unregister(mlxreg_lc->io_regs); 80562306a36Sopenharmony_ci /* Remove mux infrastructure. */ 80662306a36Sopenharmony_ci if (mlxreg_lc->mux) 80762306a36Sopenharmony_ci platform_device_unregister(mlxreg_lc->mux); 80862306a36Sopenharmony_ci} 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_cistatic int mlxreg_lc_probe(struct platform_device *pdev) 81162306a36Sopenharmony_ci{ 81262306a36Sopenharmony_ci struct mlxreg_core_hotplug_platform_data *par_pdata; 81362306a36Sopenharmony_ci struct mlxreg_core_data *data; 81462306a36Sopenharmony_ci struct mlxreg_lc *mlxreg_lc; 81562306a36Sopenharmony_ci void *regmap; 81662306a36Sopenharmony_ci int i, err; 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci data = dev_get_platdata(&pdev->dev); 81962306a36Sopenharmony_ci if (!data) 82062306a36Sopenharmony_ci return -EINVAL; 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci mlxreg_lc = devm_kzalloc(&pdev->dev, sizeof(*mlxreg_lc), GFP_KERNEL); 82362306a36Sopenharmony_ci if (!mlxreg_lc) 82462306a36Sopenharmony_ci return -ENOMEM; 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci mutex_init(&mlxreg_lc->lock); 82762306a36Sopenharmony_ci /* Set event notification callback. */ 82862306a36Sopenharmony_ci data->notifier->user_handler = mlxreg_lc_event_handler; 82962306a36Sopenharmony_ci data->notifier->handle = mlxreg_lc; 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci data->hpdev.adapter = i2c_get_adapter(data->hpdev.nr); 83262306a36Sopenharmony_ci if (!data->hpdev.adapter) { 83362306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to get adapter for bus %d\n", 83462306a36Sopenharmony_ci data->hpdev.nr); 83562306a36Sopenharmony_ci err = -EFAULT; 83662306a36Sopenharmony_ci goto i2c_get_adapter_fail; 83762306a36Sopenharmony_ci } 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci /* Create device at the top of line card I2C tree.*/ 84062306a36Sopenharmony_ci data->hpdev.client = i2c_new_client_device(data->hpdev.adapter, 84162306a36Sopenharmony_ci data->hpdev.brdinfo); 84262306a36Sopenharmony_ci if (IS_ERR(data->hpdev.client)) { 84362306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to create client %s at bus %d at addr 0x%02x\n", 84462306a36Sopenharmony_ci data->hpdev.brdinfo->type, data->hpdev.nr, data->hpdev.brdinfo->addr); 84562306a36Sopenharmony_ci err = PTR_ERR(data->hpdev.client); 84662306a36Sopenharmony_ci goto i2c_new_device_fail; 84762306a36Sopenharmony_ci } 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci regmap = devm_regmap_init_i2c(data->hpdev.client, 85062306a36Sopenharmony_ci &mlxreg_lc_regmap_conf); 85162306a36Sopenharmony_ci if (IS_ERR(regmap)) { 85262306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to create regmap for client %s at bus %d at addr 0x%02x\n", 85362306a36Sopenharmony_ci data->hpdev.brdinfo->type, data->hpdev.nr, data->hpdev.brdinfo->addr); 85462306a36Sopenharmony_ci err = PTR_ERR(regmap); 85562306a36Sopenharmony_ci goto devm_regmap_init_i2c_fail; 85662306a36Sopenharmony_ci } 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ci /* Set default registers. */ 85962306a36Sopenharmony_ci for (i = 0; i < mlxreg_lc_regmap_conf.num_reg_defaults; i++) { 86062306a36Sopenharmony_ci err = regmap_write(regmap, mlxreg_lc_regmap_default[i].reg, 86162306a36Sopenharmony_ci mlxreg_lc_regmap_default[i].def); 86262306a36Sopenharmony_ci if (err) { 86362306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to set default regmap %d for client %s at bus %d at addr 0x%02x\n", 86462306a36Sopenharmony_ci i, data->hpdev.brdinfo->type, data->hpdev.nr, 86562306a36Sopenharmony_ci data->hpdev.brdinfo->addr); 86662306a36Sopenharmony_ci goto regmap_write_fail; 86762306a36Sopenharmony_ci } 86862306a36Sopenharmony_ci } 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ci /* Sync registers with hardware. */ 87162306a36Sopenharmony_ci regcache_mark_dirty(regmap); 87262306a36Sopenharmony_ci err = regcache_sync(regmap); 87362306a36Sopenharmony_ci if (err) { 87462306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to sync regmap for client %s at bus %d at addr 0x%02x\n", 87562306a36Sopenharmony_ci data->hpdev.brdinfo->type, data->hpdev.nr, data->hpdev.brdinfo->addr); 87662306a36Sopenharmony_ci goto regcache_sync_fail; 87762306a36Sopenharmony_ci } 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_ci par_pdata = data->hpdev.brdinfo->platform_data; 88062306a36Sopenharmony_ci mlxreg_lc->par_regmap = par_pdata->regmap; 88162306a36Sopenharmony_ci mlxreg_lc->data = data; 88262306a36Sopenharmony_ci mlxreg_lc->dev = &pdev->dev; 88362306a36Sopenharmony_ci platform_set_drvdata(pdev, mlxreg_lc); 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_ci /* Configure line card. */ 88662306a36Sopenharmony_ci err = mlxreg_lc_config_init(mlxreg_lc, regmap, data); 88762306a36Sopenharmony_ci if (err) 88862306a36Sopenharmony_ci goto mlxreg_lc_config_init_fail; 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_ci return 0; 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_cimlxreg_lc_config_init_fail: 89362306a36Sopenharmony_ciregcache_sync_fail: 89462306a36Sopenharmony_ciregmap_write_fail: 89562306a36Sopenharmony_cidevm_regmap_init_i2c_fail: 89662306a36Sopenharmony_ci i2c_unregister_device(data->hpdev.client); 89762306a36Sopenharmony_ci data->hpdev.client = NULL; 89862306a36Sopenharmony_cii2c_new_device_fail: 89962306a36Sopenharmony_ci i2c_put_adapter(data->hpdev.adapter); 90062306a36Sopenharmony_ci data->hpdev.adapter = NULL; 90162306a36Sopenharmony_cii2c_get_adapter_fail: 90262306a36Sopenharmony_ci /* Clear event notification callback and handle. */ 90362306a36Sopenharmony_ci if (data->notifier) { 90462306a36Sopenharmony_ci data->notifier->user_handler = NULL; 90562306a36Sopenharmony_ci data->notifier->handle = NULL; 90662306a36Sopenharmony_ci } 90762306a36Sopenharmony_ci return err; 90862306a36Sopenharmony_ci} 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_cistatic int mlxreg_lc_remove(struct platform_device *pdev) 91162306a36Sopenharmony_ci{ 91262306a36Sopenharmony_ci struct mlxreg_core_data *data = dev_get_platdata(&pdev->dev); 91362306a36Sopenharmony_ci struct mlxreg_lc *mlxreg_lc = platform_get_drvdata(pdev); 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci mlxreg_lc_state_update_locked(mlxreg_lc, MLXREG_LC_INITIALIZED, 0); 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ci /* 91862306a36Sopenharmony_ci * Probing and removing are invoked by hotplug events raised upon line card insertion and 91962306a36Sopenharmony_ci * removing. If probing procedure fails all data is cleared. However, hotplug event still 92062306a36Sopenharmony_ci * will be raised on line card removing and activate removing procedure. In this case there 92162306a36Sopenharmony_ci * is nothing to remove. 92262306a36Sopenharmony_ci */ 92362306a36Sopenharmony_ci if (!data->notifier || !data->notifier->handle) 92462306a36Sopenharmony_ci return 0; 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_ci /* Clear event notification callback and handle. */ 92762306a36Sopenharmony_ci data->notifier->user_handler = NULL; 92862306a36Sopenharmony_ci data->notifier->handle = NULL; 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_ci /* Destroy static I2C device feeding by main power. */ 93162306a36Sopenharmony_ci mlxreg_lc_destroy_static_devices(mlxreg_lc, mlxreg_lc->main_devs, 93262306a36Sopenharmony_ci mlxreg_lc->main_devs_num); 93362306a36Sopenharmony_ci /* Destroy static I2C device feeding by auxiliary power. */ 93462306a36Sopenharmony_ci mlxreg_lc_destroy_static_devices(mlxreg_lc, mlxreg_lc->aux_devs, mlxreg_lc->aux_devs_num); 93562306a36Sopenharmony_ci /* Unregister underlying drivers. */ 93662306a36Sopenharmony_ci mlxreg_lc_config_exit(mlxreg_lc); 93762306a36Sopenharmony_ci if (data->hpdev.client) { 93862306a36Sopenharmony_ci i2c_unregister_device(data->hpdev.client); 93962306a36Sopenharmony_ci data->hpdev.client = NULL; 94062306a36Sopenharmony_ci i2c_put_adapter(data->hpdev.adapter); 94162306a36Sopenharmony_ci data->hpdev.adapter = NULL; 94262306a36Sopenharmony_ci } 94362306a36Sopenharmony_ci 94462306a36Sopenharmony_ci return 0; 94562306a36Sopenharmony_ci} 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_cistatic struct platform_driver mlxreg_lc_driver = { 94862306a36Sopenharmony_ci .probe = mlxreg_lc_probe, 94962306a36Sopenharmony_ci .remove = mlxreg_lc_remove, 95062306a36Sopenharmony_ci .driver = { 95162306a36Sopenharmony_ci .name = "mlxreg-lc", 95262306a36Sopenharmony_ci }, 95362306a36Sopenharmony_ci}; 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_cimodule_platform_driver(mlxreg_lc_driver); 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_ciMODULE_AUTHOR("Vadim Pasternak <vadimp@nvidia.com>"); 95862306a36Sopenharmony_ciMODULE_DESCRIPTION("Nvidia line card platform driver"); 95962306a36Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL"); 96062306a36Sopenharmony_ciMODULE_ALIAS("platform:mlxreg-lc"); 961