254 lines
6.0 KiB
C
254 lines
6.0 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
|
|
#include <linux/device/devres.h>
|
|
#include <linux/netlink.h>
|
|
#include <linux/sprintf.h>
|
|
#include <linux/types.h>
|
|
#include <net/devlink.h>
|
|
|
|
#include "core.h"
|
|
#include "devlink.h"
|
|
#include "dpll.h"
|
|
#include "regs.h"
|
|
|
|
/**
|
|
* zl3073x_devlink_info_get - Devlink device info callback
|
|
* @devlink: devlink structure pointer
|
|
* @req: devlink request pointer to store information
|
|
* @extack: netlink extack pointer to report errors
|
|
*
|
|
* Return: 0 on success, <0 on error
|
|
*/
|
|
static int
|
|
zl3073x_devlink_info_get(struct devlink *devlink, struct devlink_info_req *req,
|
|
struct netlink_ext_ack *extack)
|
|
{
|
|
struct zl3073x_dev *zldev = devlink_priv(devlink);
|
|
u16 id, revision, fw_ver;
|
|
char buf[16];
|
|
u32 cfg_ver;
|
|
int rc;
|
|
|
|
rc = zl3073x_read_u16(zldev, ZL_REG_ID, &id);
|
|
if (rc)
|
|
return rc;
|
|
|
|
snprintf(buf, sizeof(buf), "%X", id);
|
|
rc = devlink_info_version_fixed_put(req,
|
|
DEVLINK_INFO_VERSION_GENERIC_ASIC_ID,
|
|
buf);
|
|
if (rc)
|
|
return rc;
|
|
|
|
rc = zl3073x_read_u16(zldev, ZL_REG_REVISION, &revision);
|
|
if (rc)
|
|
return rc;
|
|
|
|
snprintf(buf, sizeof(buf), "%X", revision);
|
|
rc = devlink_info_version_fixed_put(req,
|
|
DEVLINK_INFO_VERSION_GENERIC_ASIC_REV,
|
|
buf);
|
|
if (rc)
|
|
return rc;
|
|
|
|
rc = zl3073x_read_u16(zldev, ZL_REG_FW_VER, &fw_ver);
|
|
if (rc)
|
|
return rc;
|
|
|
|
snprintf(buf, sizeof(buf), "%u", fw_ver);
|
|
rc = devlink_info_version_running_put(req,
|
|
DEVLINK_INFO_VERSION_GENERIC_FW,
|
|
buf);
|
|
if (rc)
|
|
return rc;
|
|
|
|
rc = zl3073x_read_u32(zldev, ZL_REG_CUSTOM_CONFIG_VER, &cfg_ver);
|
|
if (rc)
|
|
return rc;
|
|
|
|
/* No custom config version */
|
|
if (cfg_ver == U32_MAX)
|
|
return 0;
|
|
|
|
snprintf(buf, sizeof(buf), "%lu.%lu.%lu.%lu",
|
|
FIELD_GET(GENMASK(31, 24), cfg_ver),
|
|
FIELD_GET(GENMASK(23, 16), cfg_ver),
|
|
FIELD_GET(GENMASK(15, 8), cfg_ver),
|
|
FIELD_GET(GENMASK(7, 0), cfg_ver));
|
|
|
|
return devlink_info_version_running_put(req, "custom_cfg", buf);
|
|
}
|
|
|
|
static int
|
|
zl3073x_devlink_reload_down(struct devlink *devlink, bool netns_change,
|
|
enum devlink_reload_action action,
|
|
enum devlink_reload_limit limit,
|
|
struct netlink_ext_ack *extack)
|
|
{
|
|
struct zl3073x_dev *zldev = devlink_priv(devlink);
|
|
|
|
if (action != DEVLINK_RELOAD_ACTION_DRIVER_REINIT)
|
|
return -EOPNOTSUPP;
|
|
|
|
/* Stop normal operation */
|
|
zl3073x_dev_stop(zldev);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
zl3073x_devlink_reload_up(struct devlink *devlink,
|
|
enum devlink_reload_action action,
|
|
enum devlink_reload_limit limit,
|
|
u32 *actions_performed,
|
|
struct netlink_ext_ack *extack)
|
|
{
|
|
struct zl3073x_dev *zldev = devlink_priv(devlink);
|
|
union devlink_param_value val;
|
|
int rc;
|
|
|
|
if (action != DEVLINK_RELOAD_ACTION_DRIVER_REINIT)
|
|
return -EOPNOTSUPP;
|
|
|
|
rc = devl_param_driverinit_value_get(devlink,
|
|
DEVLINK_PARAM_GENERIC_ID_CLOCK_ID,
|
|
&val);
|
|
if (rc)
|
|
return rc;
|
|
|
|
if (zldev->clock_id != val.vu64) {
|
|
dev_dbg(zldev->dev,
|
|
"'clock_id' changed to %016llx\n", val.vu64);
|
|
zldev->clock_id = val.vu64;
|
|
}
|
|
|
|
/* Restart normal operation */
|
|
rc = zl3073x_dev_start(zldev, false);
|
|
if (rc)
|
|
dev_warn(zldev->dev, "Failed to re-start normal operation\n");
|
|
|
|
*actions_performed = BIT(DEVLINK_RELOAD_ACTION_DRIVER_REINIT);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct devlink_ops zl3073x_devlink_ops = {
|
|
.info_get = zl3073x_devlink_info_get,
|
|
.reload_actions = BIT(DEVLINK_RELOAD_ACTION_DRIVER_REINIT),
|
|
.reload_down = zl3073x_devlink_reload_down,
|
|
.reload_up = zl3073x_devlink_reload_up,
|
|
};
|
|
|
|
static void
|
|
zl3073x_devlink_free(void *ptr)
|
|
{
|
|
devlink_free(ptr);
|
|
}
|
|
|
|
/**
|
|
* zl3073x_devm_alloc - allocates zl3073x device structure
|
|
* @dev: pointer to device structure
|
|
*
|
|
* Allocates zl3073x device structure as device resource.
|
|
*
|
|
* Return: pointer to zl3073x device on success, error pointer on error
|
|
*/
|
|
struct zl3073x_dev *zl3073x_devm_alloc(struct device *dev)
|
|
{
|
|
struct zl3073x_dev *zldev;
|
|
struct devlink *devlink;
|
|
int rc;
|
|
|
|
devlink = devlink_alloc(&zl3073x_devlink_ops, sizeof(*zldev), dev);
|
|
if (!devlink)
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
/* Add devres action to free devlink device */
|
|
rc = devm_add_action_or_reset(dev, zl3073x_devlink_free, devlink);
|
|
if (rc)
|
|
return ERR_PTR(rc);
|
|
|
|
zldev = devlink_priv(devlink);
|
|
zldev->dev = dev;
|
|
dev_set_drvdata(zldev->dev, zldev);
|
|
|
|
return zldev;
|
|
}
|
|
EXPORT_SYMBOL_NS_GPL(zl3073x_devm_alloc, "ZL3073X");
|
|
|
|
static int
|
|
zl3073x_devlink_param_clock_id_validate(struct devlink *devlink, u32 id,
|
|
union devlink_param_value val,
|
|
struct netlink_ext_ack *extack)
|
|
{
|
|
if (!val.vu64) {
|
|
NL_SET_ERR_MSG_MOD(extack, "'clock_id' must be non-zero");
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct devlink_param zl3073x_devlink_params[] = {
|
|
DEVLINK_PARAM_GENERIC(CLOCK_ID, BIT(DEVLINK_PARAM_CMODE_DRIVERINIT),
|
|
NULL, NULL,
|
|
zl3073x_devlink_param_clock_id_validate),
|
|
};
|
|
|
|
static void
|
|
zl3073x_devlink_unregister(void *ptr)
|
|
{
|
|
struct devlink *devlink = priv_to_devlink(ptr);
|
|
|
|
devl_lock(devlink);
|
|
|
|
/* Unregister devlink params */
|
|
devl_params_unregister(devlink, zl3073x_devlink_params,
|
|
ARRAY_SIZE(zl3073x_devlink_params));
|
|
|
|
/* Unregister devlink instance */
|
|
devl_unregister(devlink);
|
|
|
|
devl_unlock(devlink);
|
|
}
|
|
|
|
/**
|
|
* zl3073x_devlink_register - register devlink instance and params
|
|
* @zldev: zl3073x device to register the devlink for
|
|
*
|
|
* Register the devlink instance and parameters associated with the device.
|
|
*
|
|
* Return: 0 on success, <0 on error
|
|
*/
|
|
int zl3073x_devlink_register(struct zl3073x_dev *zldev)
|
|
{
|
|
struct devlink *devlink = priv_to_devlink(zldev);
|
|
union devlink_param_value value;
|
|
int rc;
|
|
|
|
devl_lock(devlink);
|
|
|
|
/* Register devlink params */
|
|
rc = devl_params_register(devlink, zl3073x_devlink_params,
|
|
ARRAY_SIZE(zl3073x_devlink_params));
|
|
if (rc) {
|
|
devl_unlock(devlink);
|
|
|
|
return rc;
|
|
}
|
|
|
|
value.vu64 = zldev->clock_id;
|
|
devl_param_driverinit_value_set(devlink,
|
|
DEVLINK_PARAM_GENERIC_ID_CLOCK_ID,
|
|
value);
|
|
|
|
/* Register devlink instance */
|
|
devl_register(devlink);
|
|
|
|
devl_unlock(devlink);
|
|
|
|
/* Add devres action to unregister devlink device */
|
|
return devm_add_action_or_reset(zldev->dev, zl3073x_devlink_unregister,
|
|
zldev);
|
|
}
|