mirror of
https://github.com/AtlasLinux/linux.git
synced 2026-02-02 15:22:09 -08:00
dpll: zl3073x: Register DPLL devices and pins
Enumerate all available DPLL channels and registers a DPLL device for each of them. Check all input references and outputs and register DPLL pins for them. Number of registered DPLL pins depends on configuration of references and outputs. If the reference or output is configured as differential one then only one DPLL pin is registered. Both references and outputs can be also disabled from firmware configuration and in this case no DPLL pins are registered. All registrable references are registered to all available DPLL devices with exception of DPLLs that are configured in NCO (numerically controlled oscillator) mode. In this mode DPLL channel acts as PHC and cannot be locked to any reference. Device outputs are connected to one of synthesizers and each synthesizer is driven by some DPLL channel. So output pins belonging to given output are registered to DPLL device that drives associated synthesizer. Finally add kworker task to monitor async changes on all DPLL channels and input pins and to notify about them DPLL core. Output pins are not monitored as their parameters are not changed asynchronously by the device. Co-developed-by: Prathosh Satish <Prathosh.Satish@microchip.com> Signed-off-by: Prathosh Satish <Prathosh.Satish@microchip.com> Signed-off-by: Ivan Vecera <ivecera@redhat.com> Reviewed-by: Jiri Pirko <jiri@nvidia.com> Link: https://patch.msgid.link/20250704182202.1641943-9-ivecera@redhat.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
committed by
Jakub Kicinski
parent
a99a9f0ebd
commit
75a71ecc24
@@ -1,7 +1,7 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
obj-$(CONFIG_ZL3073X) += zl3073x.o
|
||||
zl3073x-objs := core.o devlink.o prop.o
|
||||
zl3073x-objs := core.o devlink.o dpll.o prop.o
|
||||
|
||||
obj-$(CONFIG_ZL3073X_I2C) += zl3073x_i2c.o
|
||||
zl3073x_i2c-objs := i2c.o
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
#include "core.h"
|
||||
#include "devlink.h"
|
||||
#include "dpll.h"
|
||||
#include "regs.h"
|
||||
|
||||
/* Chip IDs for zl30731 */
|
||||
@@ -668,6 +669,104 @@ zl3073x_dev_state_fetch(struct zl3073x_dev *zldev)
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void
|
||||
zl3073x_dev_periodic_work(struct kthread_work *work)
|
||||
{
|
||||
struct zl3073x_dev *zldev = container_of(work, struct zl3073x_dev,
|
||||
work.work);
|
||||
struct zl3073x_dpll *zldpll;
|
||||
|
||||
list_for_each_entry(zldpll, &zldev->dplls, list)
|
||||
zl3073x_dpll_changes_check(zldpll);
|
||||
|
||||
/* Run twice a second */
|
||||
kthread_queue_delayed_work(zldev->kworker, &zldev->work,
|
||||
msecs_to_jiffies(500));
|
||||
}
|
||||
|
||||
static void zl3073x_dev_dpll_fini(void *ptr)
|
||||
{
|
||||
struct zl3073x_dpll *zldpll, *next;
|
||||
struct zl3073x_dev *zldev = ptr;
|
||||
|
||||
/* Stop monitoring thread */
|
||||
if (zldev->kworker) {
|
||||
kthread_cancel_delayed_work_sync(&zldev->work);
|
||||
kthread_destroy_worker(zldev->kworker);
|
||||
zldev->kworker = NULL;
|
||||
}
|
||||
|
||||
/* Release DPLLs */
|
||||
list_for_each_entry_safe(zldpll, next, &zldev->dplls, list) {
|
||||
zl3073x_dpll_unregister(zldpll);
|
||||
list_del(&zldpll->list);
|
||||
zl3073x_dpll_free(zldpll);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
zl3073x_devm_dpll_init(struct zl3073x_dev *zldev, u8 num_dplls)
|
||||
{
|
||||
struct kthread_worker *kworker;
|
||||
struct zl3073x_dpll *zldpll;
|
||||
unsigned int i;
|
||||
int rc;
|
||||
|
||||
INIT_LIST_HEAD(&zldev->dplls);
|
||||
|
||||
/* Initialize all DPLLs */
|
||||
for (i = 0; i < num_dplls; i++) {
|
||||
zldpll = zl3073x_dpll_alloc(zldev, i);
|
||||
if (IS_ERR(zldpll)) {
|
||||
dev_err_probe(zldev->dev, PTR_ERR(zldpll),
|
||||
"Failed to alloc DPLL%u\n", i);
|
||||
rc = PTR_ERR(zldpll);
|
||||
goto error;
|
||||
}
|
||||
|
||||
rc = zl3073x_dpll_register(zldpll);
|
||||
if (rc) {
|
||||
dev_err_probe(zldev->dev, rc,
|
||||
"Failed to register DPLL%u\n", i);
|
||||
zl3073x_dpll_free(zldpll);
|
||||
goto error;
|
||||
}
|
||||
|
||||
list_add_tail(&zldpll->list, &zldev->dplls);
|
||||
}
|
||||
|
||||
/* Perform initial firmware fine phase correction */
|
||||
rc = zl3073x_dpll_init_fine_phase_adjust(zldev);
|
||||
if (rc) {
|
||||
dev_err_probe(zldev->dev, rc,
|
||||
"Failed to init fine phase correction\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Initialize monitoring thread */
|
||||
kthread_init_delayed_work(&zldev->work, zl3073x_dev_periodic_work);
|
||||
kworker = kthread_run_worker(0, "zl3073x-%s", dev_name(zldev->dev));
|
||||
if (IS_ERR(kworker)) {
|
||||
rc = PTR_ERR(kworker);
|
||||
goto error;
|
||||
}
|
||||
|
||||
zldev->kworker = kworker;
|
||||
kthread_queue_delayed_work(zldev->kworker, &zldev->work, 0);
|
||||
|
||||
/* Add devres action to release DPLL related resources */
|
||||
rc = devm_add_action_or_reset(zldev->dev, zl3073x_dev_dpll_fini, zldev);
|
||||
if (rc)
|
||||
goto error;
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
zl3073x_dev_dpll_fini(zldev);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* zl3073x_dev_probe - initialize zl3073x device
|
||||
* @zldev: pointer to zl3073x device
|
||||
@@ -740,6 +839,11 @@ int zl3073x_dev_probe(struct zl3073x_dev *zldev,
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* Register DPLL channels */
|
||||
rc = zl3073x_devm_dpll_init(zldev, chip_info->num_channels);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* Register the devlink instance and parameters */
|
||||
rc = zl3073x_devlink_register(zldev);
|
||||
if (rc)
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
#ifndef _ZL3073X_CORE_H
|
||||
#define _ZL3073X_CORE_H
|
||||
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
@@ -10,6 +12,7 @@
|
||||
|
||||
struct device;
|
||||
struct regmap;
|
||||
struct zl3073x_dpll;
|
||||
|
||||
/*
|
||||
* Hardware limits for ZL3073x chip family
|
||||
@@ -18,6 +21,10 @@ struct regmap;
|
||||
#define ZL3073X_NUM_REFS 10
|
||||
#define ZL3073X_NUM_OUTS 10
|
||||
#define ZL3073X_NUM_SYNTHS 5
|
||||
#define ZL3073X_NUM_INPUT_PINS ZL3073X_NUM_REFS
|
||||
#define ZL3073X_NUM_OUTPUT_PINS (ZL3073X_NUM_OUTS * 2)
|
||||
#define ZL3073X_NUM_PINS (ZL3073X_NUM_INPUT_PINS + \
|
||||
ZL3073X_NUM_OUTPUT_PINS)
|
||||
|
||||
/**
|
||||
* struct zl3073x_ref - input reference invariant info
|
||||
@@ -62,6 +69,9 @@ struct zl3073x_synth {
|
||||
* @ref: array of input references' invariants
|
||||
* @out: array of outs' invariants
|
||||
* @synth: array of synths' invariants
|
||||
* @dplls: list of DPLLs
|
||||
* @kworker: thread for periodic work
|
||||
* @work: periodic work
|
||||
*/
|
||||
struct zl3073x_dev {
|
||||
struct device *dev;
|
||||
@@ -73,6 +83,13 @@ struct zl3073x_dev {
|
||||
struct zl3073x_ref ref[ZL3073X_NUM_REFS];
|
||||
struct zl3073x_out out[ZL3073X_NUM_OUTS];
|
||||
struct zl3073x_synth synth[ZL3073X_NUM_SYNTHS];
|
||||
|
||||
/* DPLL channels */
|
||||
struct list_head dplls;
|
||||
|
||||
/* Monitor */
|
||||
struct kthread_worker *kworker;
|
||||
struct kthread_delayed_work work;
|
||||
};
|
||||
|
||||
struct zl3073x_chip_info {
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#include "core.h"
|
||||
#include "devlink.h"
|
||||
#include "dpll.h"
|
||||
#include "regs.h"
|
||||
|
||||
/**
|
||||
@@ -84,9 +85,16 @@ zl3073x_devlink_reload_down(struct devlink *devlink, bool netns_change,
|
||||
enum devlink_reload_limit limit,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
struct zl3073x_dev *zldev = devlink_priv(devlink);
|
||||
struct zl3073x_dpll *zldpll;
|
||||
|
||||
if (action != DEVLINK_RELOAD_ACTION_DRIVER_REINIT)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
/* Unregister all DPLLs */
|
||||
list_for_each_entry(zldpll, &zldev->dplls, list)
|
||||
zl3073x_dpll_unregister(zldpll);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -99,6 +107,7 @@ zl3073x_devlink_reload_up(struct devlink *devlink,
|
||||
{
|
||||
struct zl3073x_dev *zldev = devlink_priv(devlink);
|
||||
union devlink_param_value val;
|
||||
struct zl3073x_dpll *zldpll;
|
||||
int rc;
|
||||
|
||||
if (action != DEVLINK_RELOAD_ACTION_DRIVER_REINIT)
|
||||
@@ -116,6 +125,14 @@ zl3073x_devlink_reload_up(struct devlink *devlink,
|
||||
zldev->clock_id = val.vu64;
|
||||
}
|
||||
|
||||
/* Re-register all DPLLs */
|
||||
list_for_each_entry(zldpll, &zldev->dplls, list) {
|
||||
rc = zl3073x_dpll_register(zldpll);
|
||||
if (rc)
|
||||
dev_warn(zldev->dev,
|
||||
"Failed to re-register DPLL%u\n", zldpll->id);
|
||||
}
|
||||
|
||||
*actions_performed = BIT(DEVLINK_RELOAD_ACTION_DRIVER_REINIT);
|
||||
|
||||
return 0;
|
||||
|
||||
917
drivers/dpll/zl3073x/dpll.c
Normal file
917
drivers/dpll/zl3073x/dpll.c
Normal file
File diff suppressed because it is too large
Load Diff
42
drivers/dpll/zl3073x/dpll.h
Normal file
42
drivers/dpll/zl3073x/dpll.h
Normal file
@@ -0,0 +1,42 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
|
||||
#ifndef _ZL3073X_DPLL_H
|
||||
#define _ZL3073X_DPLL_H
|
||||
|
||||
#include <linux/dpll.h>
|
||||
#include <linux/list.h>
|
||||
|
||||
#include "core.h"
|
||||
|
||||
/**
|
||||
* struct zl3073x_dpll - ZL3073x DPLL sub-device structure
|
||||
* @list: this DPLL list entry
|
||||
* @dev: pointer to multi-function parent device
|
||||
* @id: DPLL index
|
||||
* @refsel_mode: reference selection mode
|
||||
* @forced_ref: selected reference in forced reference lock mode
|
||||
* @dpll_dev: pointer to registered DPLL device
|
||||
* @lock_status: last saved DPLL lock status
|
||||
* @pins: list of pins
|
||||
*/
|
||||
struct zl3073x_dpll {
|
||||
struct list_head list;
|
||||
struct zl3073x_dev *dev;
|
||||
u8 id;
|
||||
u8 refsel_mode;
|
||||
u8 forced_ref;
|
||||
struct dpll_device *dpll_dev;
|
||||
enum dpll_lock_status lock_status;
|
||||
struct list_head pins;
|
||||
};
|
||||
|
||||
struct zl3073x_dpll *zl3073x_dpll_alloc(struct zl3073x_dev *zldev, u8 ch);
|
||||
void zl3073x_dpll_free(struct zl3073x_dpll *zldpll);
|
||||
|
||||
int zl3073x_dpll_register(struct zl3073x_dpll *zldpll);
|
||||
void zl3073x_dpll_unregister(struct zl3073x_dpll *zldpll);
|
||||
|
||||
int zl3073x_dpll_init_fine_phase_adjust(struct zl3073x_dev *zldev);
|
||||
void zl3073x_dpll_changes_check(struct zl3073x_dpll *zldpll);
|
||||
|
||||
#endif /* _ZL3073X_DPLL_H */
|
||||
@@ -72,6 +72,42 @@
|
||||
#define ZL_REG_FW_VER ZL_REG(0, 0x05, 2)
|
||||
#define ZL_REG_CUSTOM_CONFIG_VER ZL_REG(0, 0x07, 4)
|
||||
|
||||
/*************************
|
||||
* Register Page 2, Status
|
||||
*************************/
|
||||
|
||||
#define ZL_REG_REF_MON_STATUS(_idx) \
|
||||
ZL_REG_IDX(_idx, 2, 0x02, 1, ZL3073X_NUM_REFS, 1)
|
||||
#define ZL_REF_MON_STATUS_OK 0 /* all bits zeroed */
|
||||
|
||||
#define ZL_REG_DPLL_MON_STATUS(_idx) \
|
||||
ZL_REG_IDX(_idx, 2, 0x10, 1, ZL3073X_MAX_CHANNELS, 1)
|
||||
#define ZL_DPLL_MON_STATUS_STATE GENMASK(1, 0)
|
||||
#define ZL_DPLL_MON_STATUS_STATE_ACQUIRING 0
|
||||
#define ZL_DPLL_MON_STATUS_STATE_LOCK 1
|
||||
#define ZL_DPLL_MON_STATUS_STATE_HOLDOVER 2
|
||||
#define ZL_DPLL_MON_STATUS_HO_READY BIT(2)
|
||||
|
||||
#define ZL_REG_DPLL_REFSEL_STATUS(_idx) \
|
||||
ZL_REG_IDX(_idx, 2, 0x30, 1, ZL3073X_MAX_CHANNELS, 1)
|
||||
#define ZL_DPLL_REFSEL_STATUS_REFSEL GENMASK(3, 0)
|
||||
#define ZL_DPLL_REFSEL_STATUS_STATE GENMASK(6, 4)
|
||||
#define ZL_DPLL_REFSEL_STATUS_STATE_LOCK 4
|
||||
|
||||
/***********************
|
||||
* Register Page 5, DPLL
|
||||
***********************/
|
||||
|
||||
#define ZL_REG_DPLL_MODE_REFSEL(_idx) \
|
||||
ZL_REG_IDX(_idx, 5, 0x04, 1, ZL3073X_MAX_CHANNELS, 4)
|
||||
#define ZL_DPLL_MODE_REFSEL_MODE GENMASK(2, 0)
|
||||
#define ZL_DPLL_MODE_REFSEL_MODE_FREERUN 0
|
||||
#define ZL_DPLL_MODE_REFSEL_MODE_HOLDOVER 1
|
||||
#define ZL_DPLL_MODE_REFSEL_MODE_REFLOCK 2
|
||||
#define ZL_DPLL_MODE_REFSEL_MODE_AUTO 3
|
||||
#define ZL_DPLL_MODE_REFSEL_MODE_NCO 4
|
||||
#define ZL_DPLL_MODE_REFSEL_REF GENMASK(7, 4)
|
||||
|
||||
/***********************************
|
||||
* Register Page 9, Synth and Output
|
||||
***********************************/
|
||||
@@ -81,6 +117,11 @@
|
||||
#define ZL_SYNTH_CTRL_EN BIT(0)
|
||||
#define ZL_SYNTH_CTRL_DPLL_SEL GENMASK(6, 4)
|
||||
|
||||
#define ZL_REG_SYNTH_PHASE_SHIFT_CTRL ZL_REG(9, 0x1e, 1)
|
||||
#define ZL_REG_SYNTH_PHASE_SHIFT_MASK ZL_REG(9, 0x1f, 1)
|
||||
#define ZL_REG_SYNTH_PHASE_SHIFT_INTVL ZL_REG(9, 0x20, 1)
|
||||
#define ZL_REG_SYNTH_PHASE_SHIFT_DATA ZL_REG(9, 0x21, 2)
|
||||
|
||||
#define ZL_REG_OUTPUT_CTRL(_idx) \
|
||||
ZL_REG_IDX(_idx, 9, 0x28, 1, ZL3073X_NUM_OUTS, 1)
|
||||
#define ZL_OUTPUT_CTRL_EN BIT(0)
|
||||
@@ -100,6 +141,23 @@
|
||||
#define ZL_REF_CONFIG_ENABLE BIT(0)
|
||||
#define ZL_REF_CONFIG_DIFF_EN BIT(2)
|
||||
|
||||
/********************************
|
||||
* Register Page 12, DPLL Mailbox
|
||||
********************************/
|
||||
|
||||
#define ZL_REG_DPLL_MB_MASK ZL_REG(12, 0x02, 2)
|
||||
|
||||
#define ZL_REG_DPLL_MB_SEM ZL_REG(12, 0x04, 1)
|
||||
#define ZL_DPLL_MB_SEM_WR BIT(0)
|
||||
#define ZL_DPLL_MB_SEM_RD BIT(1)
|
||||
|
||||
#define ZL_REG_DPLL_REF_PRIO(_idx) \
|
||||
ZL_REG_IDX(_idx, 12, 0x52, 1, ZL3073X_NUM_REFS / 2, 1)
|
||||
#define ZL_DPLL_REF_PRIO_REF_P GENMASK(3, 0)
|
||||
#define ZL_DPLL_REF_PRIO_REF_N GENMASK(7, 4)
|
||||
#define ZL_DPLL_REF_PRIO_MAX 14
|
||||
#define ZL_DPLL_REF_PRIO_NONE 15
|
||||
|
||||
/*********************************
|
||||
* Register Page 13, Synth Mailbox
|
||||
*********************************/
|
||||
|
||||
Reference in New Issue
Block a user