From: Kory Maincent (Dent Project) Add devlink param attributes PD692X0_DEVLINK_PARAM_ID_SAVE_CONF and PD692X0_DEVLINK_PARAM_ID_RESET_CONF to enable userspace management of the PSE's permanent configuration stored in non-volatile memory. The save_conf attribute with the '1' value allows saving the current configuration to non-volatile memory. The reset_conf attribute restores factory defaults configurations. Skip hardware configuration initialization on probe when a saved configuration is already present in non-volatile memory (detected via user byte 42). Signed-off-by: Kory Maincent --- Changes in v2: - Move on from sysfs to devlink param for userspace management. --- Documentation/networking/devlink/index.rst | 1 + Documentation/networking/devlink/pd692x0.rst | 32 +++++ drivers/net/pse-pd/pd692x0.c | 205 ++++++++++++++++++++++++++- 3 files changed, 235 insertions(+), 3 deletions(-) diff --git a/Documentation/networking/devlink/index.rst b/Documentation/networking/devlink/index.rst index 0c58e5c729d92..6db7d9b45f7aa 100644 --- a/Documentation/networking/devlink/index.rst +++ b/Documentation/networking/devlink/index.rst @@ -96,6 +96,7 @@ parameters, info versions, and other features it supports. netdevsim nfp octeontx2 + pd692x0 prestera qed sfc diff --git a/Documentation/networking/devlink/pd692x0.rst b/Documentation/networking/devlink/pd692x0.rst new file mode 100644 index 0000000000000..3f3edd0ac0361 --- /dev/null +++ b/Documentation/networking/devlink/pd692x0.rst @@ -0,0 +1,32 @@ +.. SPDX-License-Identifier: GPL-2.0 + +=========================== +PSE PD692x0 devlink support +=========================== + +This document describes the devlink features implemented by the PSE ``PD692x0`` +device drivers. + +Parameters +========== + +The ``PD692x0`` drivers implement the following driver-specific parameters. + +.. list-table:: Driver-specific parameters implemented + :widths: 5 5 5 85 + + * - Name + - Type + - Mode + - Description + * - ``save_conf`` + - bool + - runtime + - Save the current configuration to non-volatile memory using ``1`` + attribute value. + * - ``reset_conf`` + - bool + - runtime + - Reset the current and saved configuration using ``1`` attribute + value. + diff --git a/drivers/net/pse-pd/pd692x0.c b/drivers/net/pse-pd/pd692x0.c index 782b1abf94cb1..eb4b911d438b3 100644 --- a/drivers/net/pse-pd/pd692x0.c +++ b/drivers/net/pse-pd/pd692x0.c @@ -14,6 +14,7 @@ #include #include #include +#include #define PD692X0_PSE_NAME "pd692x0_pse" @@ -30,6 +31,8 @@ #define PD692X0_FW_MIN_VER 5 #define PD692X0_FW_PATCH_VER 5 +#define PD692X0_USER_BYTE 42 + enum pd692x0_fw_state { PD692X0_FW_UNKNOWN, PD692X0_FW_OK, @@ -80,6 +83,9 @@ enum { PD692X0_MSG_GET_PORT_PARAM, PD692X0_MSG_GET_POWER_BANK, PD692X0_MSG_SET_POWER_BANK, + PD692X0_MSG_SET_USER_BYTE, + PD692X0_MSG_SAVE_SYS_SETTINGS, + PD692X0_MSG_RESTORE_FACTORY, /* add new message above here */ PD692X0_MSG_CNT @@ -103,11 +109,13 @@ struct pd692x0_priv { bool last_cmd_key; unsigned long last_cmd_key_time; + bool cfg_saved; enum ethtool_c33_pse_admin_state admin_state[PD692X0_MAX_PIS]; struct regulator_dev *manager_reg[PD692X0_MAX_MANAGERS]; int manager_pw_budget[PD692X0_MAX_MANAGERS]; int nmanagers; struct pd692x0_matrix *port_matrix; + struct devlink *dl; }; /* Template list of communication messages. The non-null bytes defined here @@ -193,6 +201,24 @@ static const struct pd692x0_msg pd692x0_msg_template_list[PD692X0_MSG_CNT] = { .key = PD692X0_KEY_CMD, .sub = {0x07, 0x0b, 0x57}, }, + [PD692X0_MSG_SET_USER_BYTE] = { + .key = PD692X0_KEY_PRG, + .sub = {0x41, PD692X0_USER_BYTE}, + .data = {0x4e, 0x4e, 0x4e, 0x4e, + 0x4e, 0x4e, 0x4e, 0x4e}, + }, + [PD692X0_MSG_SAVE_SYS_SETTINGS] = { + .key = PD692X0_KEY_PRG, + .sub = {0x06, 0x0f}, + .data = {0x4e, 0x4e, 0x4e, 0x4e, + 0x4e, 0x4e, 0x4e, 0x4e}, + }, + [PD692X0_MSG_RESTORE_FACTORY] = { + .key = PD692X0_KEY_PRG, + .sub = {0x2d, 0x4e}, + .data = {0x4e, 0x4e, 0x4e, 0x4e, + 0x4e, 0x4e, 0x4e, 0x4e}, + }, }; static u8 pd692x0_build_msg(struct pd692x0_msg *msg, u8 echo) @@ -1268,9 +1294,12 @@ static int pd692x0_setup_pi_matrix(struct pse_controller_dev *pcdev) if (ret) goto err_managers_req_pw; - ret = pd692x0_hw_conf_init(priv); - if (ret) - goto err_managers_req_pw; + /* Do not init the conf if it is already saved */ + if (!priv->cfg_saved) { + ret = pd692x0_hw_conf_init(priv); + if (ret) + goto err_managers_req_pw; + } pd692x0_of_put_managers(priv, manager); kfree(manager); @@ -1727,14 +1756,148 @@ static const struct fw_upload_ops pd692x0_fw_ops = { .cleanup = pd692x0_fw_cleanup, }; +/* Devlink Params APIs */ +enum pd692x0_devlink_param_id { + PD692X0_DEVLINK_PARAM_ID_BASE = DEVLINK_PARAM_GENERIC_ID_MAX, + PD692X0_DEVLINK_PARAM_ID_SAVE_CONF, + PD692X0_DEVLINK_PARAM_ID_RESET_CONF, +}; + +struct pd692x0_devlink { + struct pd692x0_priv *priv; +}; + +static int pd692x0_dl_validate(struct devlink *devlink, u32 id, + union devlink_param_value val, + struct netlink_ext_ack *extack) +{ + if (!val.vbool) { + NL_SET_ERR_MSG_FMT(extack, "0 is not a valid value"); + return -EINVAL; + } + + return 0; +} + +static int pd692x0_dl_save_conf_set(struct devlink *devlink, u32 id, + struct devlink_param_gset_ctx *ctx, + struct netlink_ext_ack *extack) +{ + struct pd692x0_devlink *dl = devlink_priv(devlink); + struct pd692x0_priv *priv = dl->priv; + struct pd692x0_msg msg, buf = {0}; + int ret; + + mutex_lock(&priv->pcdev.lock); + ret = pd692x0_fw_unavailable(priv); + if (ret) + goto out; + + msg = pd692x0_msg_template_list[PD692X0_MSG_SET_USER_BYTE]; + ret = pd692x0_sendrecv_msg(priv, &msg, &buf); + if (ret) + goto out; + + msg = pd692x0_msg_template_list[PD692X0_MSG_SAVE_SYS_SETTINGS]; + ret = pd692x0_send_msg(priv, &msg); + if (ret) { + dev_err(&priv->client->dev, + "Failed to save the configuration (%pe)\n", + ERR_PTR(ret)); + goto out; + } + + msleep(50); /* Sleep 50ms as described in the datasheet */ + + ret = i2c_master_recv(priv->client, (u8 *)&buf, sizeof(buf)); + if (ret != sizeof(buf)) { + if (ret >= 0) + ret = -EIO; + goto out; + } + + ret = 0; + +out: + mutex_unlock(&priv->pcdev.lock); + return ret; +} + +static int pd692x0_dl_reset_conf_set(struct devlink *devlink, u32 id, + struct devlink_param_gset_ctx *ctx, + struct netlink_ext_ack *extack) +{ + struct pd692x0_devlink *dl = devlink_priv(devlink); + struct pd692x0_priv *priv = dl->priv; + struct pd692x0_msg msg, buf = {0}; + int ret; + + mutex_lock(&priv->pcdev.lock); + ret = pd692x0_fw_unavailable(priv); + if (ret) + goto out; + + msg = pd692x0_msg_template_list[PD692X0_MSG_RESTORE_FACTORY]; + ret = pd692x0_send_msg(priv, &msg); + if (ret) { + dev_err(&priv->client->dev, + "Failed to reset the configuration (%pe)\n", + ERR_PTR(ret)); + goto out; + } + + msleep(100); /* Sleep 100ms as described in the datasheet */ + + ret = i2c_master_recv(priv->client, (u8 *)&buf, sizeof(buf)); + if (ret != sizeof(buf)) { + if (ret >= 0) + ret = -EIO; + goto out; + } + + ret = pd692x0_hw_conf_init(priv); + if (ret) { + dev_err(&priv->client->dev, + "Error configuring ports matrix (%pe)\n", + ERR_PTR(ret)); + } + +out: + mutex_unlock(&priv->pcdev.lock); + return ret; +} + +static int pd692x0_dl_dummy_get(struct devlink *devlink, u32 id, + struct devlink_param_gset_ctx *ctx) +{ + return 0; +} + +static const struct devlink_param pd692x0_dl_params[] = { + DEVLINK_PARAM_DRIVER(PD692X0_DEVLINK_PARAM_ID_SAVE_CONF, + "save_conf", DEVLINK_PARAM_TYPE_BOOL, + BIT(DEVLINK_PARAM_CMODE_RUNTIME), + pd692x0_dl_dummy_get, pd692x0_dl_save_conf_set, + pd692x0_dl_validate), + DEVLINK_PARAM_DRIVER(PD692X0_DEVLINK_PARAM_ID_RESET_CONF, + "reset_conf", DEVLINK_PARAM_TYPE_BOOL, + BIT(DEVLINK_PARAM_CMODE_RUNTIME), + pd692x0_dl_dummy_get, pd692x0_dl_reset_conf_set, + pd692x0_dl_validate), +}; + +static const struct devlink_ops pd692x0_dl_ops = { }; + static int pd692x0_i2c_probe(struct i2c_client *client) { static const char * const regulators[] = { "vdd", "vdda" }; struct pd692x0_msg msg, buf = {0}, zero = {0}; + struct pd692x0_devlink *pd692x0_dl; struct device *dev = &client->dev; struct pd692x0_msg_ver ver; struct pd692x0_priv *priv; struct fw_upload *fwl; + struct devlink *dl; int ret; ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(regulators), @@ -1793,6 +1956,9 @@ static int pd692x0_i2c_probe(struct i2c_client *client) } } + if (buf.data[2] == PD692X0_USER_BYTE) + priv->cfg_saved = true; + priv->np = dev->of_node; priv->pcdev.nr_lines = PD692X0_MAX_PIS; priv->pcdev.owner = THIS_MODULE; @@ -1813,14 +1979,47 @@ static int pd692x0_i2c_probe(struct i2c_client *client) "failed to register to the Firmware Upload API\n"); priv->fwl = fwl; + dl = devlink_alloc(&pd692x0_dl_ops, + sizeof(struct pd692x0_devlink), dev); + if (!dl) { + dev_err(dev, "devlink_alloc failed\n"); + ret = -ENOMEM; + goto err_unregister_fw; + } + + pd692x0_dl = devlink_priv(dl); + pd692x0_dl->priv = priv; + priv->dl = dl; + + ret = devlink_params_register(dl, pd692x0_dl_params, + ARRAY_SIZE(pd692x0_dl_params)); + if (ret) { + dev_err(dev, + "devlink params register failed with error %d", + ret); + goto err_free_dl; + } + + devlink_register(dl); return 0; + +err_free_dl: + devlink_free(dl); +err_unregister_fw: + firmware_upload_unregister(priv->fwl); + + return ret; } static void pd692x0_i2c_remove(struct i2c_client *client) { struct pd692x0_priv *priv = i2c_get_clientdata(client); + struct devlink *dl = priv->dl; pd692x0_managers_free_pw_budget(priv); + devlink_params_unregister(dl, pd692x0_dl_params, + ARRAY_SIZE(pd692x0_dl_params)); + devlink_unregister(dl); firmware_upload_unregister(priv->fwl); } -- 2.43.0