123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960 |
- // SPDX-License-Identifier: GPL-2.0+
- /*
- * Nvidia line card driver
- *
- * Copyright (C) 2020 Nvidia Technologies Ltd.
- */
- #include <linux/device.h>
- #include <linux/i2c.h>
- #include <linux/module.h>
- #include <linux/platform_data/mlxcpld.h>
- #include <linux/platform_data/mlxreg.h>
- #include <linux/platform_device.h>
- #include <linux/regmap.h>
- /* I2C bus IO offsets */
- #define MLXREG_LC_REG_CPLD1_VER_OFFSET 0x2500
- #define MLXREG_LC_REG_FPGA1_VER_OFFSET 0x2501
- #define MLXREG_LC_REG_CPLD1_PN_OFFSET 0x2504
- #define MLXREG_LC_REG_FPGA1_PN_OFFSET 0x2506
- #define MLXREG_LC_REG_RESET_CAUSE_OFFSET 0x251d
- #define MLXREG_LC_REG_LED1_OFFSET 0x2520
- #define MLXREG_LC_REG_GP0_OFFSET 0x252e
- #define MLXREG_LC_REG_FIELD_UPGRADE 0x2534
- #define MLXREG_LC_CHANNEL_I2C_REG 0x25dc
- #define MLXREG_LC_REG_CPLD1_MVER_OFFSET 0x25de
- #define MLXREG_LC_REG_FPGA1_MVER_OFFSET 0x25df
- #define MLXREG_LC_REG_MAX_POWER_OFFSET 0x25f1
- #define MLXREG_LC_REG_CONFIG_OFFSET 0x25fb
- #define MLXREG_LC_REG_MAX 0x3fff
- /**
- * 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),
- };
- #define MLXREG_LC_CONFIGURED (MLXREG_LC_INITIALIZED | MLXREG_LC_POWERED | MLXREG_LC_SYNCED)
- /* mlxreg_lc - device private data
- * @dev: platform device;
- * @lock: line card lock;
- * @par_regmap: parent device regmap handle;
- * @data: pltaform 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;
- };
- static bool mlxreg_lc_writeable_reg(struct device *dev, unsigned int 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:
- return true;
- }
- return false;
- }
- static bool mlxreg_lc_readable_reg(struct device *dev, unsigned int 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:
- return true;
- }
- return false;
- }
- static bool mlxreg_lc_volatile_reg(struct device *dev, unsigned int 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:
- return true;
- }
- return false;
- }
- static const struct reg_default mlxreg_lc_regmap_default[] = {
- { MLXREG_LC_CHANNEL_I2C_REG, 0x00 },
- };
- /* Configuration for the register map of a device with 2 bytes address space. */
- static const struct regmap_config mlxreg_lc_regmap_conf = {
- .reg_bits = 16,
- .val_bits = 8,
- .max_register = MLXREG_LC_REG_MAX,
- .cache_type = REGCACHE_FLAT,
- .writeable_reg = mlxreg_lc_writeable_reg,
- .readable_reg = mlxreg_lc_readable_reg,
- .volatile_reg = mlxreg_lc_volatile_reg,
- .reg_defaults = mlxreg_lc_regmap_default,
- .num_reg_defaults = ARRAY_SIZE(mlxreg_lc_regmap_default),
- };
- /* Default channels vector.
- * It contains only the channels, which physically connected to the devices,
- * empty channels are skipped.
- */
- static int mlxreg_lc_chan[] = {
- 0x04, 0x05, 0x06, 0x07, 0x08, 0x10, 0x20, 0x21, 0x22, 0x23, 0x40, 0x41,
- 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d,
- 0x4e, 0x4f
- };
- /* Defaul mux configuration. */
- static struct mlxcpld_mux_plat_data mlxreg_lc_mux_data[] = {
- {
- .chan_ids = mlxreg_lc_chan,
- .num_adaps = ARRAY_SIZE(mlxreg_lc_chan),
- .sel_reg_addr = MLXREG_LC_CHANNEL_I2C_REG,
- .reg_size = 2,
- },
- };
- /* Defaul mux board info. */
- static struct i2c_board_info mlxreg_lc_mux_brdinfo = {
- I2C_BOARD_INFO("i2c-mux-mlxcpld", 0x32),
- };
- /* Line card default auxiliary power static devices. */
- static struct i2c_board_info mlxreg_lc_aux_pwr_devices[] = {
- {
- I2C_BOARD_INFO("24c32", 0x51),
- },
- {
- I2C_BOARD_INFO("24c32", 0x51),
- },
- };
- /* Line card default auxiliary power board info. */
- static struct mlxreg_hotplug_device mlxreg_lc_aux_pwr_brdinfo[] = {
- {
- .brdinfo = &mlxreg_lc_aux_pwr_devices[0],
- .nr = 3,
- },
- {
- .brdinfo = &mlxreg_lc_aux_pwr_devices[1],
- .nr = 4,
- },
- };
- /* Line card default main power static devices. */
- static struct i2c_board_info mlxreg_lc_main_pwr_devices[] = {
- {
- I2C_BOARD_INFO("mp2975", 0x62),
- },
- {
- I2C_BOARD_INFO("mp2975", 0x64),
- },
- {
- I2C_BOARD_INFO("max11603", 0x6d),
- },
- {
- I2C_BOARD_INFO("lm25066", 0x15),
- },
- };
- /* Line card default main power board info. */
- static struct mlxreg_hotplug_device mlxreg_lc_main_pwr_brdinfo[] = {
- {
- .brdinfo = &mlxreg_lc_main_pwr_devices[0],
- .nr = 0,
- },
- {
- .brdinfo = &mlxreg_lc_main_pwr_devices[1],
- .nr = 0,
- },
- {
- .brdinfo = &mlxreg_lc_main_pwr_devices[2],
- .nr = 1,
- },
- {
- .brdinfo = &mlxreg_lc_main_pwr_devices[3],
- .nr = 2,
- },
- };
- /* LED default data. */
- static struct mlxreg_core_data mlxreg_lc_led_data[] = {
- {
- .label = "status:green",
- .reg = MLXREG_LC_REG_LED1_OFFSET,
- .mask = GENMASK(7, 4),
- },
- {
- .label = "status:orange",
- .reg = MLXREG_LC_REG_LED1_OFFSET,
- .mask = GENMASK(7, 4),
- },
- };
- static struct mlxreg_core_platform_data mlxreg_lc_led = {
- .identity = "pci",
- .data = mlxreg_lc_led_data,
- .counter = ARRAY_SIZE(mlxreg_lc_led_data),
- };
- /* Default register access data. */
- static struct mlxreg_core_data mlxreg_lc_io_data[] = {
- {
- .label = "cpld1_version",
- .reg = MLXREG_LC_REG_CPLD1_VER_OFFSET,
- .bit = GENMASK(7, 0),
- .mode = 0444,
- },
- {
- .label = "fpga1_version",
- .reg = MLXREG_LC_REG_FPGA1_VER_OFFSET,
- .bit = GENMASK(7, 0),
- .mode = 0444,
- },
- {
- .label = "cpld1_pn",
- .reg = MLXREG_LC_REG_CPLD1_PN_OFFSET,
- .bit = GENMASK(15, 0),
- .mode = 0444,
- .regnum = 2,
- },
- {
- .label = "fpga1_pn",
- .reg = MLXREG_LC_REG_FPGA1_PN_OFFSET,
- .bit = GENMASK(15, 0),
- .mode = 0444,
- .regnum = 2,
- },
- {
- .label = "cpld1_version_min",
- .reg = MLXREG_LC_REG_CPLD1_MVER_OFFSET,
- .bit = GENMASK(7, 0),
- .mode = 0444,
- },
- {
- .label = "fpga1_version_min",
- .reg = MLXREG_LC_REG_FPGA1_MVER_OFFSET,
- .bit = GENMASK(7, 0),
- .mode = 0444,
- },
- {
- .label = "reset_fpga_not_done",
- .reg = MLXREG_LC_REG_RESET_CAUSE_OFFSET,
- .mask = GENMASK(7, 0) & ~BIT(1),
- .mode = 0444,
- },
- {
- .label = "reset_aux_pwr_or_ref",
- .reg = MLXREG_LC_REG_RESET_CAUSE_OFFSET,
- .mask = GENMASK(7, 0) & ~BIT(2),
- .mode = 0444,
- },
- {
- .label = "reset_dc_dc_pwr_fail",
- .reg = MLXREG_LC_REG_RESET_CAUSE_OFFSET,
- .mask = GENMASK(7, 0) & ~BIT(3),
- .mode = 0444,
- },
- {
- .label = "reset_from_chassis",
- .reg = MLXREG_LC_REG_RESET_CAUSE_OFFSET,
- .mask = GENMASK(7, 0) & ~BIT(4),
- .mode = 0444,
- },
- {
- .label = "reset_pwr_off_from_chassis",
- .reg = MLXREG_LC_REG_RESET_CAUSE_OFFSET,
- .mask = GENMASK(7, 0) & ~BIT(5),
- .mode = 0444,
- },
- {
- .label = "reset_line_card",
- .reg = MLXREG_LC_REG_RESET_CAUSE_OFFSET,
- .mask = GENMASK(7, 0) & ~BIT(6),
- .mode = 0444,
- },
- {
- .label = "reset_line_card_pwr_en",
- .reg = MLXREG_LC_REG_RESET_CAUSE_OFFSET,
- .mask = GENMASK(7, 0) & ~BIT(7),
- .mode = 0444,
- },
- {
- .label = "cpld_upgrade_en",
- .reg = MLXREG_LC_REG_FIELD_UPGRADE,
- .mask = GENMASK(7, 0) & ~BIT(0),
- .mode = 0644,
- .secured = 1,
- },
- {
- .label = "fpga_upgrade_en",
- .reg = MLXREG_LC_REG_FIELD_UPGRADE,
- .mask = GENMASK(7, 0) & ~BIT(1),
- .mode = 0644,
- .secured = 1,
- },
- {
- .label = "qsfp_pwr_en",
- .reg = MLXREG_LC_REG_GP0_OFFSET,
- .mask = GENMASK(7, 0) & ~BIT(0),
- .mode = 0644,
- },
- {
- .label = "vpd_wp",
- .reg = MLXREG_LC_REG_GP0_OFFSET,
- .mask = GENMASK(7, 0) & ~BIT(3),
- .mode = 0644,
- .secured = 1,
- },
- {
- .label = "agb_spi_burn_en",
- .reg = MLXREG_LC_REG_GP0_OFFSET,
- .mask = GENMASK(7, 0) & ~BIT(5),
- .mode = 0644,
- .secured = 1,
- },
- {
- .label = "fpga_spi_burn_en",
- .reg = MLXREG_LC_REG_GP0_OFFSET,
- .mask = GENMASK(7, 0) & ~BIT(6),
- .mode = 0644,
- .secured = 1,
- },
- {
- .label = "max_power",
- .reg = MLXREG_LC_REG_MAX_POWER_OFFSET,
- .bit = GENMASK(15, 0),
- .mode = 0444,
- .regnum = 2,
- },
- {
- .label = "config",
- .reg = MLXREG_LC_REG_CONFIG_OFFSET,
- .bit = GENMASK(15, 0),
- .mode = 0444,
- .regnum = 2,
- },
- };
- static struct mlxreg_core_platform_data mlxreg_lc_regs_io = {
- .data = mlxreg_lc_io_data,
- .counter = ARRAY_SIZE(mlxreg_lc_io_data),
- };
- static int
- 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;
- }
- static void
- 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;
- }
- }
- }
- static int mlxreg_lc_power_on_off(struct mlxreg_lc *mlxreg_lc, u8 action)
- {
- u32 regval;
- int err;
- err = regmap_read(mlxreg_lc->par_regmap, mlxreg_lc->data->reg_pwr, ®val);
- if (err)
- goto regmap_read_fail;
- if (action)
- regval |= BIT(mlxreg_lc->data->slot - 1);
- else
- regval &= ~BIT(mlxreg_lc->data->slot - 1);
- err = regmap_write(mlxreg_lc->par_regmap, mlxreg_lc->data->reg_pwr, regval);
- regmap_read_fail:
- return err;
- }
- static int mlxreg_lc_enable_disable(struct mlxreg_lc *mlxreg_lc, bool action)
- {
- u32 regval;
- int err;
- /*
- * 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;
- if (action)
- regval |= BIT(mlxreg_lc->data->slot - 1);
- else
- regval &= ~BIT(mlxreg_lc->data->slot - 1);
- err = regmap_write(mlxreg_lc->par_regmap, mlxreg_lc->data->reg_ena, regval);
- regmap_read_fail:
- return err;
- }
- static int
- mlxreg_lc_sn4800_c16_config_init(struct mlxreg_lc *mlxreg_lc, void *regmap,
- struct mlxreg_core_data *data)
- {
- struct device *dev = &data->hpdev.client->dev;
- /* 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;
- mlxreg_lc->aux_devs = devm_kmemdup(dev, mlxreg_lc_aux_pwr_brdinfo,
- sizeof(mlxreg_lc_aux_pwr_brdinfo), GFP_KERNEL);
- if (!mlxreg_lc->aux_devs)
- return -ENOMEM;
- mlxreg_lc->aux_devs_num = ARRAY_SIZE(mlxreg_lc_aux_pwr_brdinfo);
- mlxreg_lc->main_devs = devm_kmemdup(dev, mlxreg_lc_main_pwr_brdinfo,
- sizeof(mlxreg_lc_main_pwr_brdinfo), GFP_KERNEL);
- if (!mlxreg_lc->main_devs)
- return -ENOMEM;
- mlxreg_lc->main_devs_num = ARRAY_SIZE(mlxreg_lc_main_pwr_brdinfo);
- return 0;
- }
- static void
- mlxreg_lc_state_update(struct mlxreg_lc *mlxreg_lc, enum mlxreg_lc_state state, u8 action)
- {
- if (action)
- mlxreg_lc->state |= state;
- else
- mlxreg_lc->state &= ~state;
- }
- static void
- mlxreg_lc_state_update_locked(struct mlxreg_lc *mlxreg_lc, enum mlxreg_lc_state state, u8 action)
- {
- mutex_lock(&mlxreg_lc->lock);
- 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.
- */
- static int mlxreg_lc_event_handler(void *handle, enum mlxreg_hotplug_kind kind, u8 action)
- {
- struct mlxreg_lc *mlxreg_lc = handle;
- int err = 0;
- dev_info(mlxreg_lc->dev, "linecard#%d state %d event kind %d action %d\n",
- mlxreg_lc->data->slot, mlxreg_lc->state, kind, action);
- 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);
- goto mlxreg_lc_enable_disable_exit;
- }
- 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_fail;
- /* 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;
- }
- mlxreg_lc_enable_disable_exit:
- mlxreg_lc_power_on_off_fail:
- mlxreg_lc_create_static_devices_fail:
- mlxreg_lc_non_initialzed_exit:
- mutex_unlock(&mlxreg_lc->lock);
- return err;
- }
- /*
- * Callback is to be called from i2c-mux-mlxcpld driver to indicate that all adapter devices has
- * been created.
- */
- static int 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;
- mlxreg_lc_state_update_locked(mlxreg_lc, MLXREG_LC_POWERED, 1);
- }
- /* 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;
- }
- }
- mlxreg_lc_state_update_locked(mlxreg_lc, MLXREG_LC_INITIALIZED, 1);
- return 0;
- mlxreg_lc_regmap_power_on_off_fail:
- mlxreg_lc_regmap_read_sync_fail:
- if (mlxreg_lc->state & MLXREG_LC_POWERED)
- mlxreg_lc_destroy_static_devices(mlxreg_lc, mlxreg_lc->main_devs,
- mlxreg_lc->main_devs_num);
- mlxreg_lc_create_static_devices_failed:
- mlxreg_lc_destroy_static_devices(mlxreg_lc, mlxreg_lc->aux_devs, mlxreg_lc->aux_devs_num);
- mlxreg_lc_regmap_read_power_fail:
- return err;
- }
- static int
- mlxreg_lc_config_init(struct mlxreg_lc *mlxreg_lc, void *regmap,
- struct mlxreg_core_data *data)
- {
- struct device *dev = &data->hpdev.client->dev;
- int lsb, err;
- u32 regval;
- /* 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 regio 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;
- }
- static void 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);
- }
- static int 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;
- mutex_init(&mlxreg_lc->lock);
- /* Set event notification callback. */
- data->notifier->user_handler = mlxreg_lc_event_handler;
- data->notifier->handle = mlxreg_lc;
- 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;
- }
- par_pdata = data->hpdev.brdinfo->platform_data;
- mlxreg_lc->par_regmap = par_pdata->regmap;
- mlxreg_lc->data = data;
- mlxreg_lc->dev = &pdev->dev;
- platform_set_drvdata(pdev, mlxreg_lc);
- /* Configure line card. */
- err = mlxreg_lc_config_init(mlxreg_lc, regmap, data);
- if (err)
- goto mlxreg_lc_config_init_fail;
- return 0;
- mlxreg_lc_config_init_fail:
- regcache_sync_fail:
- regmap_write_fail:
- devm_regmap_init_i2c_fail:
- i2c_unregister_device(data->hpdev.client);
- data->hpdev.client = NULL;
- i2c_new_device_fail:
- i2c_put_adapter(data->hpdev.adapter);
- data->hpdev.adapter = NULL;
- i2c_get_adapter_fail:
- /* Clear event notification callback and handle. */
- if (data->notifier) {
- data->notifier->user_handler = NULL;
- data->notifier->handle = NULL;
- }
- return err;
- }
- static int mlxreg_lc_remove(struct platform_device *pdev)
- {
- struct mlxreg_core_data *data = dev_get_platdata(&pdev->dev);
- struct mlxreg_lc *mlxreg_lc = platform_get_drvdata(pdev);
- mlxreg_lc_state_update_locked(mlxreg_lc, MLXREG_LC_INITIALIZED, 0);
- /*
- * 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 0;
- /* Clear event notification callback and handle. */
- data->notifier->user_handler = NULL;
- data->notifier->handle = NULL;
- /* Destroy static I2C device feeding by main power. */
- mlxreg_lc_destroy_static_devices(mlxreg_lc, mlxreg_lc->main_devs,
- mlxreg_lc->main_devs_num);
- /* Destroy static I2C device feeding by auxiliary power. */
- mlxreg_lc_destroy_static_devices(mlxreg_lc, mlxreg_lc->aux_devs, mlxreg_lc->aux_devs_num);
- /* Unregister underlying drivers. */
- mlxreg_lc_config_exit(mlxreg_lc);
- if (data->hpdev.client) {
- i2c_unregister_device(data->hpdev.client);
- data->hpdev.client = NULL;
- i2c_put_adapter(data->hpdev.adapter);
- data->hpdev.adapter = NULL;
- }
- return 0;
- }
- static struct platform_driver mlxreg_lc_driver = {
- .probe = mlxreg_lc_probe,
- .remove = mlxreg_lc_remove,
- .driver = {
- .name = "mlxreg-lc",
- },
- };
- module_platform_driver(mlxreg_lc_driver);
- MODULE_AUTHOR("Vadim Pasternak <[email protected]>");
- MODULE_DESCRIPTION("Nvidia line card platform driver");
- MODULE_LICENSE("Dual BSD/GPL");
- MODULE_ALIAS("platform:mlxreg-lc");
|