/** * enum mlxreg_lc_type - line cards types * * @MLXREG_LC_SN4800_C16: 100GbE line card with 16 QSFP28 ports;
*/ enum mlxreg_lc_type {
MLXREG_LC_SN4800_C16 = 0x0000,
};
/** * enum mlxreg_lc_state - line cards state * * @MLXREG_LC_INITIALIZED: line card is initialized; * @MLXREG_LC_POWERED: line card is powered; * @MLXREG_LC_SYNCED: line card is synchronized between hardware and firmware;
*/ enum mlxreg_lc_state {
MLXREG_LC_INITIALIZED = BIT(0),
MLXREG_LC_POWERED = BIT(1),
MLXREG_LC_SYNCED = BIT(2),
};
/* mlxreg_lc - device private data * @dev: platform device; * @lock: line card lock; * @par_regmap: parent device regmap handle; * @data: platform core data; * @io_data: register access platform data; * @led_data: LED platform data; * @mux_data: MUX platform data; * @led: LED device; * @io_regs: register access device; * @mux_brdinfo: mux configuration; * @mux: mux devices; * @aux_devs: I2C devices feeding by auxiliary power; * @aux_devs_num: number of I2C devices feeding by auxiliary power; * @main_devs: I2C devices feeding by main power; * @main_devs_num: number of I2C devices feeding by main power; * @state: line card state;
*/ struct mlxreg_lc { struct device *dev; struct mutex lock; /* line card access lock */ void *par_regmap; struct mlxreg_core_data *data; struct mlxreg_core_platform_data *io_data; struct mlxreg_core_platform_data *led_data; struct mlxcpld_mux_plat_data *mux_data; struct platform_device *led; struct platform_device *io_regs; struct i2c_board_info *mux_brdinfo; struct platform_device *mux; struct mlxreg_hotplug_device *aux_devs; int aux_devs_num; struct mlxreg_hotplug_device *main_devs; int main_devs_num; enum mlxreg_lc_state state;
};
staticbool mlxreg_lc_writeable_reg(struct device *dev, unsignedint reg)
{ switch (reg) { case MLXREG_LC_REG_LED1_OFFSET: case MLXREG_LC_REG_GP0_OFFSET: case MLXREG_LC_REG_FIELD_UPGRADE: case MLXREG_LC_CHANNEL_I2C_REG: returntrue;
} returnfalse;
}
staticbool mlxreg_lc_readable_reg(struct device *dev, unsignedint reg)
{ switch (reg) { case MLXREG_LC_REG_CPLD1_VER_OFFSET: case MLXREG_LC_REG_FPGA1_VER_OFFSET: case MLXREG_LC_REG_CPLD1_PN_OFFSET: case MLXREG_LC_REG_FPGA1_PN_OFFSET: case MLXREG_LC_REG_RESET_CAUSE_OFFSET: case MLXREG_LC_REG_LED1_OFFSET: case MLXREG_LC_REG_GP0_OFFSET: case MLXREG_LC_REG_FIELD_UPGRADE: case MLXREG_LC_CHANNEL_I2C_REG: case MLXREG_LC_REG_CPLD1_MVER_OFFSET: case MLXREG_LC_REG_FPGA1_MVER_OFFSET: case MLXREG_LC_REG_MAX_POWER_OFFSET: case MLXREG_LC_REG_CONFIG_OFFSET: returntrue;
} returnfalse;
}
staticbool mlxreg_lc_volatile_reg(struct device *dev, unsignedint reg)
{ switch (reg) { case MLXREG_LC_REG_CPLD1_VER_OFFSET: case MLXREG_LC_REG_FPGA1_VER_OFFSET: case MLXREG_LC_REG_CPLD1_PN_OFFSET: case MLXREG_LC_REG_FPGA1_PN_OFFSET: case MLXREG_LC_REG_RESET_CAUSE_OFFSET: case MLXREG_LC_REG_LED1_OFFSET: case MLXREG_LC_REG_GP0_OFFSET: case MLXREG_LC_REG_FIELD_UPGRADE: case MLXREG_LC_CHANNEL_I2C_REG: case MLXREG_LC_REG_CPLD1_MVER_OFFSET: case MLXREG_LC_REG_FPGA1_MVER_OFFSET: case MLXREG_LC_REG_MAX_POWER_OFFSET: case MLXREG_LC_REG_CONFIG_OFFSET: returntrue;
} returnfalse;
}
staticint
mlxreg_lc_create_static_devices(struct mlxreg_lc *mlxreg_lc, struct mlxreg_hotplug_device *devs, int size)
{ struct mlxreg_hotplug_device *dev = devs; int i, ret;
/* Create static I2C device feeding by auxiliary or main power. */ for (i = 0; i < size; i++, dev++) {
dev->client = i2c_new_client_device(dev->adapter, dev->brdinfo); if (IS_ERR(dev->client)) {
dev_err(mlxreg_lc->dev, "Failed to create client %s at bus %d at addr 0x%02x\n",
dev->brdinfo->type, dev->nr, dev->brdinfo->addr);
dev->adapter = NULL;
ret = PTR_ERR(dev->client); goto fail_create_static_devices;
}
}
return 0;
fail_create_static_devices: while (--i >= 0) {
dev = devs + i;
i2c_unregister_device(dev->client);
dev->client = NULL;
} return ret;
}
staticvoid
mlxreg_lc_destroy_static_devices(struct mlxreg_lc *mlxreg_lc, struct mlxreg_hotplug_device *devs, int size)
{ struct mlxreg_hotplug_device *dev = devs; int i;
/* Destroy static I2C device feeding by auxiliary or main power. */ for (i = 0; i < size; i++, dev++) { if (dev->client) {
i2c_unregister_device(dev->client);
dev->client = NULL;
}
}
}
/* * Hardware holds the line card after powering on in the disabled state. Holding line card * in disabled state protects access to the line components, like FPGA and gearboxes. * Line card should be enabled in order to get it in operational state. Line card could be * disabled for moving it to non-operational state. Enabling line card does not affect the * line card which is already has been enabled. Disabling does not affect the disabled line * card.
*/
err = regmap_read(mlxreg_lc->par_regmap, mlxreg_lc->data->reg_ena, ®val); if (err) goto regmap_read_fail;
/* Set line card configuration according to the type. */
mlxreg_lc->mux_data = mlxreg_lc_mux_data;
mlxreg_lc->io_data = &mlxreg_lc_regs_io;
mlxreg_lc->led_data = &mlxreg_lc_led;
mlxreg_lc->mux_brdinfo = &mlxreg_lc_mux_brdinfo;
if (action)
mlxreg_lc->state |= state; else
mlxreg_lc->state &= ~state;
mutex_unlock(&mlxreg_lc->lock);
}
/* * Callback is to be called from mlxreg-hotplug driver to notify about line card about received * event.
*/ staticint mlxreg_lc_event_handler(void *handle, enum mlxreg_hotplug_kind kind, u8 action)
{ struct mlxreg_lc *mlxreg_lc = handle; int err = 0;
mutex_lock(&mlxreg_lc->lock); if (!(mlxreg_lc->state & MLXREG_LC_INITIALIZED)) goto mlxreg_lc_non_initialzed_exit;
switch (kind) { case MLXREG_HOTPLUG_LC_SYNCED: /* * Synchronization event - hardware and firmware are synchronized. Power on/off * line card - to allow/disallow main power source.
*/
mlxreg_lc_state_update(mlxreg_lc, MLXREG_LC_SYNCED, action); /* Power line card if it is not powered yet. */ if (!(mlxreg_lc->state & MLXREG_LC_POWERED) && action) {
err = mlxreg_lc_power_on_off(mlxreg_lc, 1); if (err) goto mlxreg_lc_power_on_off_fail;
} /* In case line card is configured - enable it. */ if (mlxreg_lc->state & MLXREG_LC_CONFIGURED && action)
err = mlxreg_lc_enable_disable(mlxreg_lc, 1); break; case MLXREG_HOTPLUG_LC_POWERED: /* Power event - attach or de-attach line card device feeding by the main power. */ if (action) { /* Do not create devices, if line card is already powered. */ if (mlxreg_lc->state & MLXREG_LC_POWERED) { /* In case line card is configured - enable it. */ if (mlxreg_lc->state & MLXREG_LC_CONFIGURED)
err = mlxreg_lc_enable_disable(mlxreg_lc, 1);
/* In case line card is already in ready state - enable it. */ if (mlxreg_lc->state & MLXREG_LC_CONFIGURED)
err = mlxreg_lc_enable_disable(mlxreg_lc, 1);
} else {
mlxreg_lc_destroy_static_devices(mlxreg_lc, mlxreg_lc->main_devs,
mlxreg_lc->main_devs_num);
}
mlxreg_lc_state_update(mlxreg_lc, MLXREG_LC_POWERED, action); break; case MLXREG_HOTPLUG_LC_READY: /* * Ready event – enable line card by releasing it from reset or disable it by put * to reset state.
*/
err = mlxreg_lc_enable_disable(mlxreg_lc, !!action); break; case MLXREG_HOTPLUG_LC_THERMAL: /* Thermal shutdown event – power off line card. */ if (action)
err = mlxreg_lc_power_on_off(mlxreg_lc, 0); break; default: break;
}
/* * Callback is to be called from i2c-mux-mlxcpld driver to indicate that all adapter devices has * been created.
*/ staticint mlxreg_lc_completion_notify(void *handle, struct i2c_adapter *parent, struct i2c_adapter *adapters[])
{ struct mlxreg_hotplug_device *main_dev, *aux_dev; struct mlxreg_lc *mlxreg_lc = handle;
u32 regval; int i, err;
/* Update I2C devices feeding by auxiliary power. */
aux_dev = mlxreg_lc->aux_devs; for (i = 0; i < mlxreg_lc->aux_devs_num; i++, aux_dev++) {
aux_dev->adapter = adapters[aux_dev->nr];
aux_dev->nr = adapters[aux_dev->nr]->nr;
}
err = mlxreg_lc_create_static_devices(mlxreg_lc, mlxreg_lc->aux_devs,
mlxreg_lc->aux_devs_num); if (err) return err;
/* Update I2C devices feeding by main power. */
main_dev = mlxreg_lc->main_devs; for (i = 0; i < mlxreg_lc->main_devs_num; i++, main_dev++) {
main_dev->adapter = adapters[main_dev->nr];
main_dev->nr = adapters[main_dev->nr]->nr;
}
/* Verify if line card is powered. */
err = regmap_read(mlxreg_lc->par_regmap, mlxreg_lc->data->reg_pwr, ®val); if (err) goto mlxreg_lc_regmap_read_power_fail;
if (regval & mlxreg_lc->data->mask) {
err = mlxreg_lc_create_static_devices(mlxreg_lc, mlxreg_lc->main_devs,
mlxreg_lc->main_devs_num); if (err) goto mlxreg_lc_create_static_devices_failed;
/* Verify if line card is synchronized. */
err = regmap_read(mlxreg_lc->par_regmap, mlxreg_lc->data->reg_sync, ®val); if (err) goto mlxreg_lc_regmap_read_sync_fail;
/* Power on line card if necessary. */ if (regval & mlxreg_lc->data->mask) {
mlxreg_lc->state |= MLXREG_LC_SYNCED;
mlxreg_lc_state_update_locked(mlxreg_lc, MLXREG_LC_SYNCED, 1); if (!(mlxreg_lc->state & MLXREG_LC_POWERED)) {
err = mlxreg_lc_power_on_off(mlxreg_lc, 1); if (err) goto mlxreg_lc_regmap_power_on_off_fail;
}
}
/* Validate line card type. */
err = regmap_read(regmap, MLXREG_LC_REG_CONFIG_OFFSET, &lsb);
err = (!err) ? regmap_read(regmap, MLXREG_LC_REG_CONFIG_OFFSET, ®val) : err; if (err) return err;
regval = (regval & GENMASK(7, 0)) << 8 | (lsb & GENMASK(7, 0)); switch (regval) { case MLXREG_LC_SN4800_C16:
err = mlxreg_lc_sn4800_c16_config_init(mlxreg_lc, regmap, data); if (err) {
dev_err(dev, "Failed to config client %s at bus %d at addr 0x%02x\n",
data->hpdev.brdinfo->type, data->hpdev.nr,
data->hpdev.brdinfo->addr); return err;
} break; default: return -ENODEV;
}
/* Create mux infrastructure. */
mlxreg_lc->mux_data->handle = mlxreg_lc;
mlxreg_lc->mux_data->completion_notify = mlxreg_lc_completion_notify;
mlxreg_lc->mux_brdinfo->platform_data = mlxreg_lc->mux_data;
mlxreg_lc->mux = platform_device_register_resndata(dev, "i2c-mux-mlxcpld", data->hpdev.nr,
NULL, 0, mlxreg_lc->mux_data, sizeof(*mlxreg_lc->mux_data)); if (IS_ERR(mlxreg_lc->mux)) {
dev_err(dev, "Failed to create mux infra for client %s at bus %d at addr 0x%02x\n",
data->hpdev.brdinfo->type, data->hpdev.nr, data->hpdev.brdinfo->addr); return PTR_ERR(mlxreg_lc->mux);
}
/* Register IO access driver. */ if (mlxreg_lc->io_data) {
mlxreg_lc->io_data->regmap = regmap;
mlxreg_lc->io_regs =
platform_device_register_resndata(dev, "mlxreg-io", data->hpdev.nr, NULL, 0,
mlxreg_lc->io_data, sizeof(*mlxreg_lc->io_data)); if (IS_ERR(mlxreg_lc->io_regs)) {
dev_err(dev, "Failed to create region for client %s at bus %d at addr 0x%02x\n",
data->hpdev.brdinfo->type, data->hpdev.nr,
data->hpdev.brdinfo->addr);
err = PTR_ERR(mlxreg_lc->io_regs); goto fail_register_io;
}
}
/* Register LED driver. */ if (mlxreg_lc->led_data) {
mlxreg_lc->led_data->regmap = regmap;
mlxreg_lc->led =
platform_device_register_resndata(dev, "leds-mlxreg", data->hpdev.nr, NULL, 0,
mlxreg_lc->led_data, sizeof(*mlxreg_lc->led_data)); if (IS_ERR(mlxreg_lc->led)) {
dev_err(dev, "Failed to create LED objects for client %s at bus %d at addr 0x%02x\n",
data->hpdev.brdinfo->type, data->hpdev.nr,
data->hpdev.brdinfo->addr);
err = PTR_ERR(mlxreg_lc->led); goto fail_register_led;
}
}
return 0;
fail_register_led: if (mlxreg_lc->io_regs)
platform_device_unregister(mlxreg_lc->io_regs);
fail_register_io: if (mlxreg_lc->mux)
platform_device_unregister(mlxreg_lc->mux);
return err;
}
staticvoid mlxreg_lc_config_exit(struct mlxreg_lc *mlxreg_lc)
{ /* Unregister LED driver. */ if (mlxreg_lc->led)
platform_device_unregister(mlxreg_lc->led); /* Unregister IO access driver. */ if (mlxreg_lc->io_regs)
platform_device_unregister(mlxreg_lc->io_regs); /* Remove mux infrastructure. */ if (mlxreg_lc->mux)
platform_device_unregister(mlxreg_lc->mux);
}
staticint mlxreg_lc_probe(struct platform_device *pdev)
{ struct mlxreg_core_hotplug_platform_data *par_pdata; struct mlxreg_core_data *data; struct mlxreg_lc *mlxreg_lc; void *regmap; int i, err;
data = dev_get_platdata(&pdev->dev); if (!data) return -EINVAL;
mlxreg_lc = devm_kzalloc(&pdev->dev, sizeof(*mlxreg_lc), GFP_KERNEL); if (!mlxreg_lc) return -ENOMEM;
data->hpdev.adapter = i2c_get_adapter(data->hpdev.nr); if (!data->hpdev.adapter) {
dev_err(&pdev->dev, "Failed to get adapter for bus %d\n",
data->hpdev.nr);
err = -EFAULT; goto i2c_get_adapter_fail;
}
/* Create device at the top of line card I2C tree.*/
data->hpdev.client = i2c_new_client_device(data->hpdev.adapter,
data->hpdev.brdinfo); if (IS_ERR(data->hpdev.client)) {
dev_err(&pdev->dev, "Failed to create client %s at bus %d at addr 0x%02x\n",
data->hpdev.brdinfo->type, data->hpdev.nr, data->hpdev.brdinfo->addr);
err = PTR_ERR(data->hpdev.client); goto i2c_new_device_fail;
}
regmap = devm_regmap_init_i2c(data->hpdev.client,
&mlxreg_lc_regmap_conf); if (IS_ERR(regmap)) {
dev_err(&pdev->dev, "Failed to create regmap for client %s at bus %d at addr 0x%02x\n",
data->hpdev.brdinfo->type, data->hpdev.nr, data->hpdev.brdinfo->addr);
err = PTR_ERR(regmap); goto devm_regmap_init_i2c_fail;
}
/* Set default registers. */ for (i = 0; i < mlxreg_lc_regmap_conf.num_reg_defaults; i++) {
err = regmap_write(regmap, mlxreg_lc_regmap_default[i].reg,
mlxreg_lc_regmap_default[i].def); if (err) {
dev_err(&pdev->dev, "Failed to set default regmap %d for client %s at bus %d at addr 0x%02x\n",
i, data->hpdev.brdinfo->type, data->hpdev.nr,
data->hpdev.brdinfo->addr); goto regmap_write_fail;
}
}
/* Sync registers with hardware. */
regcache_mark_dirty(regmap);
err = regcache_sync(regmap); if (err) {
dev_err(&pdev->dev, "Failed to sync regmap for client %s at bus %d at addr 0x%02x\n",
data->hpdev.brdinfo->type, data->hpdev.nr, data->hpdev.brdinfo->addr); goto regcache_sync_fail;
}
/* * Probing and removing are invoked by hotplug events raised upon line card insertion and * removing. If probing procedure fails all data is cleared. However, hotplug event still * will be raised on line card removing and activate removing procedure. In this case there * is nothing to remove.
*/ if (!data->notifier || !data->notifier->handle) return;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.