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, &regval);
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, &regval);
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, &regval);
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, &regval);
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, &regval) : 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