Files
linux-cryptodev-2.6/drivers/dpll/zl3073x/devlink.c
Ivan Vecera 2df8e64e01 dpll: Add basic Microchip ZL3073x support
Microchip Azurite ZL3073x represents chip family providing DPLL
and optionally PHC (PTP) functionality. The chips can be connected
be connected over I2C or SPI bus.

They have the following characteristics:
* up to 5 separate DPLL units (channels)
* 5 synthesizers
* 10 input pins (references)
* 10 outputs
* 20 output pins (output pin pair shares one output)
* Each reference and output can operate in either differential or
  single-ended mode (differential mode uses 2 pins)
* Each output is connected to one of the synthesizers
* Each synthesizer is driven by one of the DPLL unit

The device uses 7-bit addresses and 8-bits values. It exposes 8-, 16-,
32- and 48-bits registers in address range <0x000,0x77F>. Due to 7bit
addressing, the range is organized into pages of 128 bytes, with each
page containing a page selector register at address 0x7F.
For reading/writing multi-byte registers, the device supports bulk
transfers.

Add basic functionality to access device registers, probe functionality
both I2C and SPI cases and add devlink support to provide info and
to set clock ID parameter.

Signed-off-by: Ivan Vecera <ivecera@redhat.com>
Reviewed-by: Jiri Pirko <jiri@nvidia.com>
Link: https://patch.msgid.link/20250704182202.1641943-6-ivecera@redhat.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2025-07-09 19:08:52 -07:00

243 lines
5.7 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 "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)
{
if (action != DEVLINK_RELOAD_ACTION_DRIVER_REINIT)
return -EOPNOTSUPP;
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;
}
*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);
}