Merge branch 'dpll-core-improvements-and-ice-e825-c-synce-support'

Ivan Vecera says:

====================
dpll: Core improvements and ice E825-C SyncE support

This series introduces Synchronous Ethernet (SyncE) support for the Intel
E825-C Ethernet controller. Unlike previous generations where DPLL
connections were implicitly assumed, the E825-C architecture relies
on the platform firmware (ACPI) to describe the physical connections
between the Ethernet controller and external DPLLs (such as the ZL3073x).

To accommodate this, the series extends the DPLL subsystem to support
firmware node (fwnode) associations, asynchronous discovery via notifiers,
and dynamic pin management. Additionally, a significant refactor of
the DPLL reference counting logic is included to ensure robustness and
debuggability.

DPLL Core Extensions:
* Firmware Node Association: Pins can now be associated with a struct
  fwnode_handle after allocation via dpll_pin_fwnode_set(). This allows
  drivers to link pin objects with their corresponding DT/ACPI nodes.
* Asynchronous Notifiers: A raw notifier chain is added to the DPLL core.
  This allows the Ethernet driver to subscribe to events and react when
  the platform DPLL driver registers the parent pins, resolving probe
  ordering dependencies.
* Dynamic Indexing: Drivers can now request DPLL_PIN_IDX_UNSPEC to have
  the core automatically allocate a unique pin index.

Reference Counting & Debugging:
* Refactor: The reference counting logic in the core is consolidated.
  Internal list management helpers now automatically handle hold/put
  operations, removing fragile open-coded logic in the registration paths.
* Reference Tracking: A new Kconfig option DPLL_REFCNT_TRACKER is added.
  This allows developers to instrument and debug reference leaks by
  recording stack traces for every get/put operation.

Driver Updates:
* zl3073x: Updated to associate pins with fwnode handles using the new
  setter and support the 'mux' pin type.
* ice: Implements the E825-C specific hardware configuration for SyncE
  (CGU registers). It utilizes the new notifier and fwnode APIs to
  dynamically discover and attach to the platform DPLLs.

Patch Summary:
Patch 1: DPLL Core (fwnode association).
Patch 2: Driver zl3073x (Set fwnode).
Patch 3-4: DPLL Core (Notifiers and dynamic IDs).
Patch 5: Driver zl3073x (Mux type).
Patch 6: DPLL Core (Refcount refactor).
Patch 7-8: Refcount tracking infrastructure and driver updates.
Patch 9: Driver ice (E825-C SyncE logic).
====================

Link: https://patch.msgid.link/20260203174002.705176-1-ivecera@redhat.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
Paolo Abeni
2026-02-05 15:57:48 +01:00
18 changed files with 1350 additions and 150 deletions
+15
View File
@@ -8,6 +8,21 @@ menu "DPLL device support"
config DPLL
bool
config DPLL_REFCNT_TRACKER
bool "DPLL reference count tracking"
depends on DEBUG_KERNEL && STACKTRACE_SUPPORT && DPLL
select REF_TRACKER
help
Enable reference count tracking for DPLL devices and pins.
This helps debugging reference leaks and use-after-free bugs
by recording stack traces for each get/put operation.
The tracking information is exposed via debugfs at:
/sys/kernel/debug/ref_tracker/dpll_device_*
/sys/kernel/debug/ref_tracker/dpll_pin_*
If unsure, say N.
source "drivers/dpll/zl3073x/Kconfig"
endmenu
+259 -29
View File
@@ -10,6 +10,8 @@
#include <linux/device.h>
#include <linux/err.h>
#include <linux/idr.h>
#include <linux/property.h>
#include <linux/slab.h>
#include <linux/string.h>
@@ -22,6 +24,9 @@ DEFINE_MUTEX(dpll_lock);
DEFINE_XARRAY_FLAGS(dpll_device_xa, XA_FLAGS_ALLOC);
DEFINE_XARRAY_FLAGS(dpll_pin_xa, XA_FLAGS_ALLOC);
static RAW_NOTIFIER_HEAD(dpll_notifier_chain);
static DEFINE_IDA(dpll_pin_idx_ida);
static u32 dpll_device_xa_id;
static u32 dpll_pin_xa_id;
@@ -36,6 +41,7 @@ struct dpll_device_registration {
struct list_head list;
const struct dpll_device_ops *ops;
void *priv;
dpll_tracker tracker;
};
struct dpll_pin_registration {
@@ -43,8 +49,117 @@ struct dpll_pin_registration {
const struct dpll_pin_ops *ops;
void *priv;
void *cookie;
dpll_tracker tracker;
};
static int call_dpll_notifiers(unsigned long action, void *info)
{
lockdep_assert_held(&dpll_lock);
return raw_notifier_call_chain(&dpll_notifier_chain, action, info);
}
void dpll_device_notify(struct dpll_device *dpll, unsigned long action)
{
struct dpll_device_notifier_info info = {
.dpll = dpll,
.id = dpll->id,
.idx = dpll->device_idx,
.clock_id = dpll->clock_id,
.type = dpll->type,
};
call_dpll_notifiers(action, &info);
}
void dpll_pin_notify(struct dpll_pin *pin, unsigned long action)
{
struct dpll_pin_notifier_info info = {
.pin = pin,
.id = pin->id,
.idx = pin->pin_idx,
.clock_id = pin->clock_id,
.fwnode = pin->fwnode,
.prop = &pin->prop,
};
call_dpll_notifiers(action, &info);
}
static void dpll_device_tracker_alloc(struct dpll_device *dpll,
dpll_tracker *tracker)
{
#ifdef CONFIG_DPLL_REFCNT_TRACKER
ref_tracker_alloc(&dpll->refcnt_tracker, tracker, GFP_KERNEL);
#endif
}
static void dpll_device_tracker_free(struct dpll_device *dpll,
dpll_tracker *tracker)
{
#ifdef CONFIG_DPLL_REFCNT_TRACKER
ref_tracker_free(&dpll->refcnt_tracker, tracker);
#endif
}
static void __dpll_device_hold(struct dpll_device *dpll, dpll_tracker *tracker)
{
dpll_device_tracker_alloc(dpll, tracker);
refcount_inc(&dpll->refcount);
}
static void __dpll_device_put(struct dpll_device *dpll, dpll_tracker *tracker)
{
dpll_device_tracker_free(dpll, tracker);
if (refcount_dec_and_test(&dpll->refcount)) {
ASSERT_DPLL_NOT_REGISTERED(dpll);
WARN_ON_ONCE(!xa_empty(&dpll->pin_refs));
xa_destroy(&dpll->pin_refs);
xa_erase(&dpll_device_xa, dpll->id);
WARN_ON(!list_empty(&dpll->registration_list));
ref_tracker_dir_exit(&dpll->refcnt_tracker);
kfree(dpll);
}
}
static void dpll_pin_tracker_alloc(struct dpll_pin *pin, dpll_tracker *tracker)
{
#ifdef CONFIG_DPLL_REFCNT_TRACKER
ref_tracker_alloc(&pin->refcnt_tracker, tracker, GFP_KERNEL);
#endif
}
static void dpll_pin_tracker_free(struct dpll_pin *pin, dpll_tracker *tracker)
{
#ifdef CONFIG_DPLL_REFCNT_TRACKER
ref_tracker_free(&pin->refcnt_tracker, tracker);
#endif
}
static void __dpll_pin_hold(struct dpll_pin *pin, dpll_tracker *tracker)
{
dpll_pin_tracker_alloc(pin, tracker);
refcount_inc(&pin->refcount);
}
static void dpll_pin_idx_free(u32 pin_idx);
static void dpll_pin_prop_free(struct dpll_pin_properties *prop);
static void __dpll_pin_put(struct dpll_pin *pin, dpll_tracker *tracker)
{
dpll_pin_tracker_free(pin, tracker);
if (refcount_dec_and_test(&pin->refcount)) {
xa_erase(&dpll_pin_xa, pin->id);
xa_destroy(&pin->dpll_refs);
xa_destroy(&pin->parent_refs);
xa_destroy(&pin->ref_sync_pins);
dpll_pin_prop_free(&pin->prop);
fwnode_handle_put(pin->fwnode);
dpll_pin_idx_free(pin->pin_idx);
ref_tracker_dir_exit(&pin->refcnt_tracker);
kfree_rcu(pin, rcu);
}
}
struct dpll_device *dpll_device_get_by_id(int id)
{
if (xa_get_mark(&dpll_device_xa, id, DPLL_REGISTERED))
@@ -114,6 +229,7 @@ dpll_xa_ref_pin_add(struct xarray *xa_pins, struct dpll_pin *pin,
reg->ops = ops;
reg->priv = priv;
reg->cookie = cookie;
__dpll_pin_hold(pin, &reg->tracker);
if (ref_exists)
refcount_inc(&ref->refcount);
list_add_tail(&reg->list, &ref->registration_list);
@@ -136,6 +252,7 @@ static int dpll_xa_ref_pin_del(struct xarray *xa_pins, struct dpll_pin *pin,
if (WARN_ON(!reg))
return -EINVAL;
list_del(&reg->list);
__dpll_pin_put(pin, &reg->tracker);
kfree(reg);
if (refcount_dec_and_test(&ref->refcount)) {
xa_erase(xa_pins, i);
@@ -193,6 +310,7 @@ dpll_xa_ref_dpll_add(struct xarray *xa_dplls, struct dpll_device *dpll,
reg->ops = ops;
reg->priv = priv;
reg->cookie = cookie;
__dpll_device_hold(dpll, &reg->tracker);
if (ref_exists)
refcount_inc(&ref->refcount);
list_add_tail(&reg->list, &ref->registration_list);
@@ -215,6 +333,7 @@ dpll_xa_ref_dpll_del(struct xarray *xa_dplls, struct dpll_device *dpll,
if (WARN_ON(!reg))
return;
list_del(&reg->list);
__dpll_device_put(dpll, &reg->tracker);
kfree(reg);
if (refcount_dec_and_test(&ref->refcount)) {
xa_erase(xa_dplls, i);
@@ -256,6 +375,7 @@ dpll_device_alloc(const u64 clock_id, u32 device_idx, struct module *module)
return ERR_PTR(ret);
}
xa_init_flags(&dpll->pin_refs, XA_FLAGS_ALLOC);
ref_tracker_dir_init(&dpll->refcnt_tracker, 128, "dpll_device");
return dpll;
}
@@ -265,6 +385,7 @@ dpll_device_alloc(const u64 clock_id, u32 device_idx, struct module *module)
* @clock_id: clock_id of creator
* @device_idx: idx given by device driver
* @module: reference to registering module
* @tracker: tracking object for the acquired reference
*
* Get existing object of a dpll device, unique for given arguments.
* Create new if doesn't exist yet.
@@ -275,7 +396,8 @@ dpll_device_alloc(const u64 clock_id, u32 device_idx, struct module *module)
* * ERR_PTR(X) - error
*/
struct dpll_device *
dpll_device_get(u64 clock_id, u32 device_idx, struct module *module)
dpll_device_get(u64 clock_id, u32 device_idx, struct module *module,
dpll_tracker *tracker)
{
struct dpll_device *dpll, *ret = NULL;
unsigned long index;
@@ -285,13 +407,17 @@ dpll_device_get(u64 clock_id, u32 device_idx, struct module *module)
if (dpll->clock_id == clock_id &&
dpll->device_idx == device_idx &&
dpll->module == module) {
__dpll_device_hold(dpll, tracker);
ret = dpll;
refcount_inc(&ret->refcount);
break;
}
}
if (!ret)
if (!ret) {
ret = dpll_device_alloc(clock_id, device_idx, module);
if (!IS_ERR(ret))
dpll_device_tracker_alloc(ret, tracker);
}
mutex_unlock(&dpll_lock);
return ret;
@@ -301,22 +427,16 @@ EXPORT_SYMBOL_GPL(dpll_device_get);
/**
* dpll_device_put - decrease the refcount and free memory if possible
* @dpll: dpll_device struct pointer
* @tracker: tracking object for the acquired reference
*
* Context: Acquires a lock (dpll_lock)
* Drop reference for a dpll device, if all references are gone, delete
* dpll device object.
*/
void dpll_device_put(struct dpll_device *dpll)
void dpll_device_put(struct dpll_device *dpll, dpll_tracker *tracker)
{
mutex_lock(&dpll_lock);
if (refcount_dec_and_test(&dpll->refcount)) {
ASSERT_DPLL_NOT_REGISTERED(dpll);
WARN_ON_ONCE(!xa_empty(&dpll->pin_refs));
xa_destroy(&dpll->pin_refs);
xa_erase(&dpll_device_xa, dpll->id);
WARN_ON(!list_empty(&dpll->registration_list));
kfree(dpll);
}
__dpll_device_put(dpll, tracker);
mutex_unlock(&dpll_lock);
}
EXPORT_SYMBOL_GPL(dpll_device_put);
@@ -378,6 +498,7 @@ int dpll_device_register(struct dpll_device *dpll, enum dpll_type type,
reg->ops = ops;
reg->priv = priv;
dpll->type = type;
__dpll_device_hold(dpll, &reg->tracker);
first_registration = list_empty(&dpll->registration_list);
list_add_tail(&reg->list, &dpll->registration_list);
if (!first_registration) {
@@ -417,6 +538,7 @@ void dpll_device_unregister(struct dpll_device *dpll,
return;
}
list_del(&reg->list);
__dpll_device_put(dpll, &reg->tracker);
kfree(reg);
if (!list_empty(&dpll->registration_list)) {
@@ -428,6 +550,36 @@ void dpll_device_unregister(struct dpll_device *dpll,
}
EXPORT_SYMBOL_GPL(dpll_device_unregister);
static int dpll_pin_idx_alloc(u32 *pin_idx)
{
int ret;
if (!pin_idx)
return -EINVAL;
/* Alloc unique number from IDA. Number belongs to <0, INT_MAX> range */
ret = ida_alloc(&dpll_pin_idx_ida, GFP_KERNEL);
if (ret < 0)
return ret;
/* Map the value to dynamic pin index range <INT_MAX+1, U32_MAX> */
*pin_idx = (u32)ret + INT_MAX + 1;
return 0;
}
static void dpll_pin_idx_free(u32 pin_idx)
{
if (pin_idx <= INT_MAX)
return; /* Not a dynamic pin index */
/* Map the index value from dynamic pin index range to IDA range and
* free it.
*/
pin_idx -= (u32)INT_MAX + 1;
ida_free(&dpll_pin_idx_ida, pin_idx);
}
static void dpll_pin_prop_free(struct dpll_pin_properties *prop)
{
kfree(prop->package_label);
@@ -485,9 +637,18 @@ dpll_pin_alloc(u64 clock_id, u32 pin_idx, struct module *module,
struct dpll_pin *pin;
int ret;
if (pin_idx == DPLL_PIN_IDX_UNSPEC) {
ret = dpll_pin_idx_alloc(&pin_idx);
if (ret)
return ERR_PTR(ret);
} else if (pin_idx > INT_MAX) {
return ERR_PTR(-EINVAL);
}
pin = kzalloc(sizeof(*pin), GFP_KERNEL);
if (!pin)
return ERR_PTR(-ENOMEM);
if (!pin) {
ret = -ENOMEM;
goto err_pin_alloc;
}
pin->pin_idx = pin_idx;
pin->clock_id = clock_id;
pin->module = module;
@@ -507,6 +668,7 @@ dpll_pin_alloc(u64 clock_id, u32 pin_idx, struct module *module,
&dpll_pin_xa_id, GFP_KERNEL);
if (ret < 0)
goto err_xa_alloc;
ref_tracker_dir_init(&pin->refcnt_tracker, 128, "dpll_pin");
return pin;
err_xa_alloc:
xa_destroy(&pin->dpll_refs);
@@ -515,6 +677,8 @@ err_xa_alloc:
dpll_pin_prop_free(&pin->prop);
err_pin_prop:
kfree(pin);
err_pin_alloc:
dpll_pin_idx_free(pin_idx);
return ERR_PTR(ret);
}
@@ -538,12 +702,35 @@ void dpll_netdev_pin_clear(struct net_device *dev)
}
EXPORT_SYMBOL(dpll_netdev_pin_clear);
int register_dpll_notifier(struct notifier_block *nb)
{
int ret;
mutex_lock(&dpll_lock);
ret = raw_notifier_chain_register(&dpll_notifier_chain, nb);
mutex_unlock(&dpll_lock);
return ret;
}
EXPORT_SYMBOL_GPL(register_dpll_notifier);
int unregister_dpll_notifier(struct notifier_block *nb)
{
int ret;
mutex_lock(&dpll_lock);
ret = raw_notifier_chain_unregister(&dpll_notifier_chain, nb);
mutex_unlock(&dpll_lock);
return ret;
}
EXPORT_SYMBOL_GPL(unregister_dpll_notifier);
/**
* dpll_pin_get - find existing or create new dpll pin
* @clock_id: clock_id of creator
* @pin_idx: idx given by dev driver
* @module: reference to registering module
* @prop: dpll pin properties
* @tracker: tracking object for the acquired reference
*
* Get existing object of a pin (unique for given arguments) or create new
* if doesn't exist yet.
@@ -555,7 +742,7 @@ EXPORT_SYMBOL(dpll_netdev_pin_clear);
*/
struct dpll_pin *
dpll_pin_get(u64 clock_id, u32 pin_idx, struct module *module,
const struct dpll_pin_properties *prop)
const struct dpll_pin_properties *prop, dpll_tracker *tracker)
{
struct dpll_pin *pos, *ret = NULL;
unsigned long i;
@@ -565,13 +752,16 @@ dpll_pin_get(u64 clock_id, u32 pin_idx, struct module *module,
if (pos->clock_id == clock_id &&
pos->pin_idx == pin_idx &&
pos->module == module) {
__dpll_pin_hold(pos, tracker);
ret = pos;
refcount_inc(&ret->refcount);
break;
}
}
if (!ret)
if (!ret) {
ret = dpll_pin_alloc(clock_id, pin_idx, module, prop);
if (!IS_ERR(ret))
dpll_pin_tracker_alloc(ret, tracker);
}
mutex_unlock(&dpll_lock);
return ret;
@@ -581,26 +771,69 @@ EXPORT_SYMBOL_GPL(dpll_pin_get);
/**
* dpll_pin_put - decrease the refcount and free memory if possible
* @pin: pointer to a pin to be put
* @tracker: tracking object for the acquired reference
*
* Drop reference for a pin, if all references are gone, delete pin object.
*
* Context: Acquires a lock (dpll_lock)
*/
void dpll_pin_put(struct dpll_pin *pin)
void dpll_pin_put(struct dpll_pin *pin, dpll_tracker *tracker)
{
mutex_lock(&dpll_lock);
if (refcount_dec_and_test(&pin->refcount)) {
xa_erase(&dpll_pin_xa, pin->id);
xa_destroy(&pin->dpll_refs);
xa_destroy(&pin->parent_refs);
xa_destroy(&pin->ref_sync_pins);
dpll_pin_prop_free(&pin->prop);
kfree_rcu(pin, rcu);
}
__dpll_pin_put(pin, tracker);
mutex_unlock(&dpll_lock);
}
EXPORT_SYMBOL_GPL(dpll_pin_put);
/**
* dpll_pin_fwnode_set - set dpll pin firmware node reference
* @pin: pointer to a dpll pin
* @fwnode: firmware node handle
*
* Set firmware node handle for the given dpll pin.
*/
void dpll_pin_fwnode_set(struct dpll_pin *pin, struct fwnode_handle *fwnode)
{
mutex_lock(&dpll_lock);
fwnode_handle_put(pin->fwnode); /* Drop fwnode previously set */
pin->fwnode = fwnode_handle_get(fwnode);
mutex_unlock(&dpll_lock);
}
EXPORT_SYMBOL_GPL(dpll_pin_fwnode_set);
/**
* fwnode_dpll_pin_find - find dpll pin by firmware node reference
* @fwnode: reference to firmware node
* @tracker: tracking object for the acquired reference
*
* Get existing object of a pin that is associated with given firmware node
* reference.
*
* Context: Acquires a lock (dpll_lock)
* Return:
* * valid dpll_pin pointer on success
* * NULL when no such pin exists
*/
struct dpll_pin *fwnode_dpll_pin_find(struct fwnode_handle *fwnode,
dpll_tracker *tracker)
{
struct dpll_pin *pin, *ret = NULL;
unsigned long index;
mutex_lock(&dpll_lock);
xa_for_each(&dpll_pin_xa, index, pin) {
if (pin->fwnode == fwnode) {
__dpll_pin_hold(pin, tracker);
ret = pin;
break;
}
}
mutex_unlock(&dpll_lock);
return ret;
}
EXPORT_SYMBOL_GPL(fwnode_dpll_pin_find);
static int
__dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin,
const struct dpll_pin_ops *ops, void *priv, void *cookie)
@@ -743,7 +976,6 @@ int dpll_pin_on_pin_register(struct dpll_pin *parent, struct dpll_pin *pin,
ret = dpll_xa_ref_pin_add(&pin->parent_refs, parent, ops, priv, pin);
if (ret)
goto unlock;
refcount_inc(&pin->refcount);
xa_for_each(&parent->dpll_refs, i, ref) {
ret = __dpll_pin_register(ref->dpll, pin, ops, priv, parent);
if (ret) {
@@ -763,7 +995,6 @@ dpll_unregister:
parent);
dpll_pin_delete_ntf(pin);
}
refcount_dec(&pin->refcount);
dpll_xa_ref_pin_del(&pin->parent_refs, parent, ops, priv, pin);
unlock:
mutex_unlock(&dpll_lock);
@@ -790,7 +1021,6 @@ void dpll_pin_on_pin_unregister(struct dpll_pin *parent, struct dpll_pin *pin,
mutex_lock(&dpll_lock);
dpll_pin_delete_ntf(pin);
dpll_xa_ref_pin_del(&pin->parent_refs, parent, ops, priv, pin);
refcount_dec(&pin->refcount);
xa_for_each(&pin->dpll_refs, i, ref)
__dpll_pin_unregister(ref->dpll, pin, ops, priv, parent);
mutex_unlock(&dpll_lock);
+11
View File
@@ -10,6 +10,7 @@
#include <linux/dpll.h>
#include <linux/list.h>
#include <linux/refcount.h>
#include <linux/ref_tracker.h>
#include "dpll_nl.h"
#define DPLL_REGISTERED XA_MARK_1
@@ -23,6 +24,7 @@
* @type: type of a dpll
* @pin_refs: stores pins registered within a dpll
* @refcount: refcount
* @refcnt_tracker: ref_tracker directory for debugging reference leaks
* @registration_list: list of registered ops and priv data of dpll owners
**/
struct dpll_device {
@@ -33,6 +35,7 @@ struct dpll_device {
enum dpll_type type;
struct xarray pin_refs;
refcount_t refcount;
struct ref_tracker_dir refcnt_tracker;
struct list_head registration_list;
};
@@ -42,11 +45,13 @@ struct dpll_device {
* @pin_idx: index of a pin given by dev driver
* @clock_id: clock_id of creator
* @module: module of creator
* @fwnode: optional reference to firmware node
* @dpll_refs: hold referencees to dplls pin was registered with
* @parent_refs: hold references to parent pins pin was registered with
* @ref_sync_pins: hold references to pins for Reference SYNC feature
* @prop: pin properties copied from the registerer
* @refcount: refcount
* @refcnt_tracker: ref_tracker directory for debugging reference leaks
* @rcu: rcu_head for kfree_rcu()
**/
struct dpll_pin {
@@ -54,11 +59,13 @@ struct dpll_pin {
u32 pin_idx;
u64 clock_id;
struct module *module;
struct fwnode_handle *fwnode;
struct xarray dpll_refs;
struct xarray parent_refs;
struct xarray ref_sync_pins;
struct dpll_pin_properties prop;
refcount_t refcount;
struct ref_tracker_dir refcnt_tracker;
struct rcu_head rcu;
};
@@ -89,4 +96,8 @@ struct dpll_pin_ref *dpll_xa_ref_dpll_first(struct xarray *xa_refs);
extern struct xarray dpll_device_xa;
extern struct xarray dpll_pin_xa;
extern struct mutex dpll_lock;
void dpll_device_notify(struct dpll_device *dpll, unsigned long action);
void dpll_pin_notify(struct dpll_pin *pin, unsigned long action);
#endif
+6
View File
@@ -761,17 +761,20 @@ err_free_msg:
int dpll_device_create_ntf(struct dpll_device *dpll)
{
dpll_device_notify(dpll, DPLL_DEVICE_CREATED);
return dpll_device_event_send(DPLL_CMD_DEVICE_CREATE_NTF, dpll);
}
int dpll_device_delete_ntf(struct dpll_device *dpll)
{
dpll_device_notify(dpll, DPLL_DEVICE_DELETED);
return dpll_device_event_send(DPLL_CMD_DEVICE_DELETE_NTF, dpll);
}
static int
__dpll_device_change_ntf(struct dpll_device *dpll)
{
dpll_device_notify(dpll, DPLL_DEVICE_CHANGED);
return dpll_device_event_send(DPLL_CMD_DEVICE_CHANGE_NTF, dpll);
}
@@ -829,16 +832,19 @@ err_free_msg:
int dpll_pin_create_ntf(struct dpll_pin *pin)
{
dpll_pin_notify(pin, DPLL_PIN_CREATED);
return dpll_pin_event_send(DPLL_CMD_PIN_CREATE_NTF, pin);
}
int dpll_pin_delete_ntf(struct dpll_pin *pin)
{
dpll_pin_notify(pin, DPLL_PIN_DELETED);
return dpll_pin_event_send(DPLL_CMD_PIN_DELETE_NTF, pin);
}
int __dpll_pin_change_ntf(struct dpll_pin *pin)
{
dpll_pin_notify(pin, DPLL_PIN_CHANGED);
return dpll_pin_event_send(DPLL_CMD_PIN_CHANGE_NTF, pin);
}
+9 -6
View File
@@ -29,6 +29,7 @@
* @list: this DPLL pin list entry
* @dpll: DPLL the pin is registered to
* @dpll_pin: pointer to registered dpll_pin
* @tracker: tracking object for the acquired reference
* @label: package label
* @dir: pin direction
* @id: pin id
@@ -44,6 +45,7 @@ struct zl3073x_dpll_pin {
struct list_head list;
struct zl3073x_dpll *dpll;
struct dpll_pin *dpll_pin;
dpll_tracker tracker;
char label[8];
enum dpll_pin_direction dir;
u8 id;
@@ -1480,11 +1482,12 @@ zl3073x_dpll_pin_register(struct zl3073x_dpll_pin *pin, u32 index)
/* Create or get existing DPLL pin */
pin->dpll_pin = dpll_pin_get(zldpll->dev->clock_id, index, THIS_MODULE,
&props->dpll_props);
&props->dpll_props, &pin->tracker);
if (IS_ERR(pin->dpll_pin)) {
rc = PTR_ERR(pin->dpll_pin);
goto err_pin_get;
}
dpll_pin_fwnode_set(pin->dpll_pin, props->fwnode);
if (zl3073x_dpll_is_input_pin(pin))
ops = &zl3073x_dpll_input_pin_ops;
@@ -1502,7 +1505,7 @@ zl3073x_dpll_pin_register(struct zl3073x_dpll_pin *pin, u32 index)
return 0;
err_register:
dpll_pin_put(pin->dpll_pin);
dpll_pin_put(pin->dpll_pin, &pin->tracker);
err_prio_get:
pin->dpll_pin = NULL;
err_pin_get:
@@ -1533,7 +1536,7 @@ zl3073x_dpll_pin_unregister(struct zl3073x_dpll_pin *pin)
/* Unregister the pin */
dpll_pin_unregister(zldpll->dpll_dev, pin->dpll_pin, ops, pin);
dpll_pin_put(pin->dpll_pin);
dpll_pin_put(pin->dpll_pin, &pin->tracker);
pin->dpll_pin = NULL;
}
@@ -1707,7 +1710,7 @@ zl3073x_dpll_device_register(struct zl3073x_dpll *zldpll)
dpll_mode_refsel);
zldpll->dpll_dev = dpll_device_get(zldev->clock_id, zldpll->id,
THIS_MODULE);
THIS_MODULE, &zldpll->tracker);
if (IS_ERR(zldpll->dpll_dev)) {
rc = PTR_ERR(zldpll->dpll_dev);
zldpll->dpll_dev = NULL;
@@ -1719,7 +1722,7 @@ zl3073x_dpll_device_register(struct zl3073x_dpll *zldpll)
zl3073x_prop_dpll_type_get(zldev, zldpll->id),
&zl3073x_dpll_device_ops, zldpll);
if (rc) {
dpll_device_put(zldpll->dpll_dev);
dpll_device_put(zldpll->dpll_dev, &zldpll->tracker);
zldpll->dpll_dev = NULL;
}
@@ -1742,7 +1745,7 @@ zl3073x_dpll_device_unregister(struct zl3073x_dpll *zldpll)
dpll_device_unregister(zldpll->dpll_dev, &zl3073x_dpll_device_ops,
zldpll);
dpll_device_put(zldpll->dpll_dev);
dpll_device_put(zldpll->dpll_dev, &zldpll->tracker);
zldpll->dpll_dev = NULL;
}
+2
View File
@@ -18,6 +18,7 @@
* @check_count: periodic check counter
* @phase_monitor: is phase offset monitor enabled
* @dpll_dev: pointer to registered DPLL device
* @tracker: tracking object for the acquired reference
* @lock_status: last saved DPLL lock status
* @pins: list of pins
* @change_work: device change notification work
@@ -31,6 +32,7 @@ struct zl3073x_dpll {
u8 check_count;
bool phase_monitor;
struct dpll_device *dpll_dev;
dpll_tracker tracker;
enum dpll_lock_status lock_status;
struct list_head pins;
struct work_struct change_work;
+2
View File
@@ -249,6 +249,8 @@ struct zl3073x_pin_props *zl3073x_pin_props_get(struct zl3073x_dev *zldev,
props->dpll_props.type = DPLL_PIN_TYPE_INT_OSCILLATOR;
else if (!strcmp(type, "synce"))
props->dpll_props.type = DPLL_PIN_TYPE_SYNCE_ETH_PORT;
else if (!strcmp(type, "mux"))
props->dpll_props.type = DPLL_PIN_TYPE_MUX;
else
dev_warn(zldev->dev,
"Unknown or unsupported pin type '%s'\n",
File diff suppressed because it is too large Load Diff
+30
View File
@@ -20,9 +20,16 @@ enum ice_dpll_pin_sw {
ICE_DPLL_PIN_SW_NUM
};
struct ice_dpll_pin_work {
struct work_struct work;
unsigned long action;
struct ice_dpll_pin *pin;
};
/** ice_dpll_pin - store info about pins
* @pin: dpll pin structure
* @pf: pointer to pf, which has registered the dpll_pin
* @tracker: reference count tracker
* @idx: ice pin private idx
* @num_parents: hols number of parent pins
* @parent_idx: hold indexes of parent pins
@@ -37,6 +44,9 @@ enum ice_dpll_pin_sw {
struct ice_dpll_pin {
struct dpll_pin *pin;
struct ice_pf *pf;
dpll_tracker tracker;
struct fwnode_handle *fwnode;
struct notifier_block nb;
u8 idx;
u8 num_parents;
u8 parent_idx[ICE_DPLL_RCLK_NUM_MAX];
@@ -58,6 +68,7 @@ struct ice_dpll_pin {
/** ice_dpll - store info required for DPLL control
* @dpll: pointer to dpll dev
* @pf: pointer to pf, which has registered the dpll_device
* @tracker: reference count tracker
* @dpll_idx: index of dpll on the NIC
* @input_idx: currently selected input index
* @prev_input_idx: previously selected input index
@@ -76,6 +87,7 @@ struct ice_dpll_pin {
struct ice_dpll {
struct dpll_device *dpll;
struct ice_pf *pf;
dpll_tracker tracker;
u8 dpll_idx;
u8 input_idx;
u8 prev_input_idx;
@@ -114,7 +126,9 @@ struct ice_dpll {
struct ice_dplls {
struct kthread_worker *kworker;
struct kthread_delayed_work work;
struct workqueue_struct *wq;
struct mutex lock;
struct completion dpll_init;
struct ice_dpll eec;
struct ice_dpll pps;
struct ice_dpll_pin *inputs;
@@ -143,3 +157,19 @@ static inline void ice_dpll_deinit(struct ice_pf *pf) { }
#endif
#endif
#define ICE_CGU_R10 0x28
#define ICE_CGU_R10_SYNCE_CLKO_SEL GENMASK(8, 5)
#define ICE_CGU_R10_SYNCE_CLKODIV_M1 GENMASK(13, 9)
#define ICE_CGU_R10_SYNCE_CLKODIV_LOAD BIT(14)
#define ICE_CGU_R10_SYNCE_DCK_RST BIT(15)
#define ICE_CGU_R10_SYNCE_ETHCLKO_SEL GENMASK(18, 16)
#define ICE_CGU_R10_SYNCE_ETHDIV_M1 GENMASK(23, 19)
#define ICE_CGU_R10_SYNCE_ETHDIV_LOAD BIT(24)
#define ICE_CGU_R10_SYNCE_DCK2_RST BIT(25)
#define ICE_CGU_R10_SYNCE_S_REF_CLK GENMASK(31, 27)
#define ICE_CGU_R11 0x2C
#define ICE_CGU_R11_SYNCE_S_BYP_CLK GENMASK(6, 1)
#define ICE_CGU_BYPASS_MUX_OFFSET_E825C 3
+3
View File
@@ -3989,6 +3989,9 @@ void ice_init_feature_support(struct ice_pf *pf)
break;
}
if (pf->hw.mac_type == ICE_MAC_GENERIC_3K_E825)
ice_set_feature_support(pf, ICE_F_PHY_RCLK);
if (pf->hw.mac_type == ICE_MAC_E830) {
ice_set_feature_support(pf, ICE_F_MBX_LIMIT);
ice_set_feature_support(pf, ICE_F_GCS);
+32
View File
@@ -1341,6 +1341,38 @@ void ice_ptp_link_change(struct ice_pf *pf, bool linkup)
if (pf->hw.reset_ongoing)
return;
if (hw->mac_type == ICE_MAC_GENERIC_3K_E825) {
int pin, err;
if (!test_bit(ICE_FLAG_DPLL, pf->flags))
return;
mutex_lock(&pf->dplls.lock);
for (pin = 0; pin < ICE_SYNCE_CLK_NUM; pin++) {
enum ice_synce_clk clk_pin;
bool active;
u8 port_num;
port_num = ptp_port->port_num;
clk_pin = (enum ice_synce_clk)pin;
err = ice_tspll_bypass_mux_active_e825c(hw,
port_num,
&active,
clk_pin);
if (WARN_ON_ONCE(err)) {
mutex_unlock(&pf->dplls.lock);
return;
}
err = ice_tspll_cfg_synce_ethdiv_e825c(hw, clk_pin);
if (active && WARN_ON_ONCE(err)) {
mutex_unlock(&pf->dplls.lock);
return;
}
}
mutex_unlock(&pf->dplls.lock);
}
switch (hw->mac_type) {
case ICE_MAC_E810:
case ICE_MAC_E830:
+8 -1
View File
@@ -5903,7 +5903,14 @@ int ice_get_cgu_rclk_pin_info(struct ice_hw *hw, u8 *base_idx, u8 *pin_num)
*base_idx = SI_REF1P;
else
ret = -ENODEV;
break;
case ICE_DEV_ID_E825C_BACKPLANE:
case ICE_DEV_ID_E825C_QSFP:
case ICE_DEV_ID_E825C_SFP:
case ICE_DEV_ID_E825C_SGMII:
*pin_num = ICE_SYNCE_CLK_NUM;
*base_idx = 0;
ret = 0;
break;
default:
ret = -ENODEV;
+217
View File
@@ -624,3 +624,220 @@ int ice_tspll_init(struct ice_hw *hw)
return err;
}
/**
* ice_tspll_bypass_mux_active_e825c - check if the given port is set active
* @hw: Pointer to the HW struct
* @port: Number of the port
* @active: Output flag showing if port is active
* @output: Output pin, we have two in E825C
*
* Check if given port is selected as recovered clock source for given output.
*
* Return:
* * 0 - success
* * negative - error
*/
int ice_tspll_bypass_mux_active_e825c(struct ice_hw *hw, u8 port, bool *active,
enum ice_synce_clk output)
{
u8 active_clk;
u32 val;
int err;
switch (output) {
case ICE_SYNCE_CLK0:
err = ice_read_cgu_reg(hw, ICE_CGU_R10, &val);
if (err)
return err;
active_clk = FIELD_GET(ICE_CGU_R10_SYNCE_S_REF_CLK, val);
break;
case ICE_SYNCE_CLK1:
err = ice_read_cgu_reg(hw, ICE_CGU_R11, &val);
if (err)
return err;
active_clk = FIELD_GET(ICE_CGU_R11_SYNCE_S_BYP_CLK, val);
break;
default:
return -EINVAL;
}
if (active_clk == port % hw->ptp.ports_per_phy +
ICE_CGU_BYPASS_MUX_OFFSET_E825C)
*active = true;
else
*active = false;
return 0;
}
/**
* ice_tspll_cfg_bypass_mux_e825c - configure reference clock mux
* @hw: Pointer to the HW struct
* @ena: true to enable the reference, false if disable
* @port_num: Number of the port
* @output: Output pin, we have two in E825C
*
* Set reference clock source and output clock selection.
*
* Context: Called under pf->dplls.lock
* Return:
* * 0 - success
* * negative - error
*/
int ice_tspll_cfg_bypass_mux_e825c(struct ice_hw *hw, bool ena, u32 port_num,
enum ice_synce_clk output)
{
u8 first_mux;
int err;
u32 r10;
err = ice_read_cgu_reg(hw, ICE_CGU_R10, &r10);
if (err)
return err;
if (!ena)
first_mux = ICE_CGU_NET_REF_CLK0;
else
first_mux = port_num + ICE_CGU_BYPASS_MUX_OFFSET_E825C;
r10 &= ~(ICE_CGU_R10_SYNCE_DCK_RST | ICE_CGU_R10_SYNCE_DCK2_RST);
switch (output) {
case ICE_SYNCE_CLK0:
r10 &= ~(ICE_CGU_R10_SYNCE_ETHCLKO_SEL |
ICE_CGU_R10_SYNCE_ETHDIV_LOAD |
ICE_CGU_R10_SYNCE_S_REF_CLK);
r10 |= FIELD_PREP(ICE_CGU_R10_SYNCE_S_REF_CLK, first_mux);
r10 |= FIELD_PREP(ICE_CGU_R10_SYNCE_ETHCLKO_SEL,
ICE_CGU_REF_CLK_BYP0_DIV);
break;
case ICE_SYNCE_CLK1:
{
u32 val;
err = ice_read_cgu_reg(hw, ICE_CGU_R11, &val);
if (err)
return err;
val &= ~ICE_CGU_R11_SYNCE_S_BYP_CLK;
val |= FIELD_PREP(ICE_CGU_R11_SYNCE_S_BYP_CLK, first_mux);
err = ice_write_cgu_reg(hw, ICE_CGU_R11, val);
if (err)
return err;
r10 &= ~(ICE_CGU_R10_SYNCE_CLKODIV_LOAD |
ICE_CGU_R10_SYNCE_CLKO_SEL);
r10 |= FIELD_PREP(ICE_CGU_R10_SYNCE_CLKO_SEL,
ICE_CGU_REF_CLK_BYP1_DIV);
break;
}
default:
return -EINVAL;
}
err = ice_write_cgu_reg(hw, ICE_CGU_R10, r10);
if (err)
return err;
return 0;
}
/**
* ice_tspll_get_div_e825c - get the divider for the given speed
* @link_speed: link speed of the port
* @divider: output value, calculated divider
*
* Get CGU divider value based on the link speed.
*
* Return:
* * 0 - success
* * negative - error
*/
static int ice_tspll_get_div_e825c(u16 link_speed, unsigned int *divider)
{
switch (link_speed) {
case ICE_AQ_LINK_SPEED_100GB:
case ICE_AQ_LINK_SPEED_50GB:
case ICE_AQ_LINK_SPEED_25GB:
*divider = 10;
break;
case ICE_AQ_LINK_SPEED_40GB:
case ICE_AQ_LINK_SPEED_10GB:
*divider = 4;
break;
case ICE_AQ_LINK_SPEED_5GB:
case ICE_AQ_LINK_SPEED_2500MB:
case ICE_AQ_LINK_SPEED_1000MB:
*divider = 2;
break;
case ICE_AQ_LINK_SPEED_100MB:
*divider = 1;
break;
default:
return -EOPNOTSUPP;
}
return 0;
}
/**
* ice_tspll_cfg_synce_ethdiv_e825c - set the divider on the mux
* @hw: Pointer to the HW struct
* @output: Output pin, we have two in E825C
*
* Set the correct CGU divider for RCLKA or RCLKB.
*
* Context: Called under pf->dplls.lock
* Return:
* * 0 - success
* * negative - error
*/
int ice_tspll_cfg_synce_ethdiv_e825c(struct ice_hw *hw,
enum ice_synce_clk output)
{
unsigned int divider;
u16 link_speed;
u32 val;
int err;
link_speed = hw->port_info->phy.link_info.link_speed;
if (!link_speed)
return 0;
err = ice_tspll_get_div_e825c(link_speed, &divider);
if (err)
return err;
err = ice_read_cgu_reg(hw, ICE_CGU_R10, &val);
if (err)
return err;
/* programmable divider value (from 2 to 16) minus 1 for ETHCLKOUT */
switch (output) {
case ICE_SYNCE_CLK0:
val &= ~(ICE_CGU_R10_SYNCE_ETHDIV_M1 |
ICE_CGU_R10_SYNCE_ETHDIV_LOAD);
val |= FIELD_PREP(ICE_CGU_R10_SYNCE_ETHDIV_M1, divider - 1);
err = ice_write_cgu_reg(hw, ICE_CGU_R10, val);
if (err)
return err;
val |= ICE_CGU_R10_SYNCE_ETHDIV_LOAD;
break;
case ICE_SYNCE_CLK1:
val &= ~(ICE_CGU_R10_SYNCE_CLKODIV_M1 |
ICE_CGU_R10_SYNCE_CLKODIV_LOAD);
val |= FIELD_PREP(ICE_CGU_R10_SYNCE_CLKODIV_M1, divider - 1);
err = ice_write_cgu_reg(hw, ICE_CGU_R10, val);
if (err)
return err;
val |= ICE_CGU_R10_SYNCE_CLKODIV_LOAD;
break;
default:
return -EINVAL;
}
err = ice_write_cgu_reg(hw, ICE_CGU_R10, val);
if (err)
return err;
return 0;
}
+12 -1
View File
@@ -21,11 +21,22 @@ struct ice_tspll_params_e82x {
u32 frac_n_div;
};
#define ICE_CGU_NET_REF_CLK0 0x0
#define ICE_CGU_REF_CLK_BYP0 0x5
#define ICE_CGU_REF_CLK_BYP0_DIV 0x0
#define ICE_CGU_REF_CLK_BYP1 0x4
#define ICE_CGU_REF_CLK_BYP1_DIV 0x1
#define ICE_TSPLL_CK_REFCLKFREQ_E825 0x1F
#define ICE_TSPLL_NDIVRATIO_E825 5
#define ICE_TSPLL_FBDIV_INTGR_E825 256
int ice_tspll_cfg_pps_out_e825c(struct ice_hw *hw, bool enable);
int ice_tspll_init(struct ice_hw *hw);
int ice_tspll_bypass_mux_active_e825c(struct ice_hw *hw, u8 port, bool *active,
enum ice_synce_clk output);
int ice_tspll_cfg_bypass_mux_e825c(struct ice_hw *hw, bool ena, u32 port_num,
enum ice_synce_clk output);
int ice_tspll_cfg_synce_ethdiv_e825c(struct ice_hw *hw,
enum ice_synce_clk output);
#endif /* _ICE_TSPLL_H_ */
@@ -349,6 +349,12 @@ enum ice_clk_src {
NUM_ICE_CLK_SRC
};
enum ice_synce_clk {
ICE_SYNCE_CLK0,
ICE_SYNCE_CLK1,
ICE_SYNCE_CLK_NUM
};
struct ice_ts_func_info {
/* Function specific info */
enum ice_tspll_freq time_ref;
+10 -6
View File
@@ -9,7 +9,9 @@
*/
struct mlx5_dpll {
struct dpll_device *dpll;
dpll_tracker dpll_tracker;
struct dpll_pin *dpll_pin;
dpll_tracker pin_tracker;
struct mlx5_core_dev *mdev;
struct workqueue_struct *wq;
struct delayed_work work;
@@ -438,7 +440,8 @@ static int mlx5_dpll_probe(struct auxiliary_device *adev,
auxiliary_set_drvdata(adev, mdpll);
/* Multiple mdev instances might share one DPLL device. */
mdpll->dpll = dpll_device_get(clock_id, 0, THIS_MODULE);
mdpll->dpll = dpll_device_get(clock_id, 0, THIS_MODULE,
&mdpll->dpll_tracker);
if (IS_ERR(mdpll->dpll)) {
err = PTR_ERR(mdpll->dpll);
goto err_free_mdpll;
@@ -451,7 +454,8 @@ static int mlx5_dpll_probe(struct auxiliary_device *adev,
/* Multiple mdev instances might share one DPLL pin. */
mdpll->dpll_pin = dpll_pin_get(clock_id, mlx5_get_dev_index(mdev),
THIS_MODULE, &mlx5_dpll_pin_properties);
THIS_MODULE, &mlx5_dpll_pin_properties,
&mdpll->pin_tracker);
if (IS_ERR(mdpll->dpll_pin)) {
err = PTR_ERR(mdpll->dpll_pin);
goto err_unregister_dpll_device;
@@ -479,11 +483,11 @@ err_unregister_dpll_pin:
dpll_pin_unregister(mdpll->dpll, mdpll->dpll_pin,
&mlx5_dpll_pins_ops, mdpll);
err_put_dpll_pin:
dpll_pin_put(mdpll->dpll_pin);
dpll_pin_put(mdpll->dpll_pin, &mdpll->pin_tracker);
err_unregister_dpll_device:
dpll_device_unregister(mdpll->dpll, &mlx5_dpll_device_ops, mdpll);
err_put_dpll_device:
dpll_device_put(mdpll->dpll);
dpll_device_put(mdpll->dpll, &mdpll->dpll_tracker);
err_free_mdpll:
kfree(mdpll);
return err;
@@ -499,9 +503,9 @@ static void mlx5_dpll_remove(struct auxiliary_device *adev)
destroy_workqueue(mdpll->wq);
dpll_pin_unregister(mdpll->dpll, mdpll->dpll_pin,
&mlx5_dpll_pins_ops, mdpll);
dpll_pin_put(mdpll->dpll_pin);
dpll_pin_put(mdpll->dpll_pin, &mdpll->pin_tracker);
dpll_device_unregister(mdpll->dpll, &mlx5_dpll_device_ops, mdpll);
dpll_device_put(mdpll->dpll);
dpll_device_put(mdpll->dpll, &mdpll->dpll_tracker);
kfree(mdpll);
mlx5_dpll_synce_status_set(mdev,
+11 -7
View File
@@ -285,6 +285,7 @@ struct ptp_ocp_sma_connector {
u8 default_fcn;
struct dpll_pin *dpll_pin;
struct dpll_pin_properties dpll_prop;
dpll_tracker tracker;
};
struct ocp_attr_group {
@@ -383,6 +384,7 @@ struct ptp_ocp {
struct ptp_ocp_sma_connector sma[OCP_SMA_NUM];
const struct ocp_sma_op *sma_op;
struct dpll_device *dpll;
dpll_tracker tracker;
int signals_nr;
int freq_in_nr;
};
@@ -4788,7 +4790,7 @@ ptp_ocp_probe(struct pci_dev *pdev, const struct pci_device_id *id)
devlink_register(devlink);
clkid = pci_get_dsn(pdev);
bp->dpll = dpll_device_get(clkid, 0, THIS_MODULE);
bp->dpll = dpll_device_get(clkid, 0, THIS_MODULE, &bp->tracker);
if (IS_ERR(bp->dpll)) {
err = PTR_ERR(bp->dpll);
dev_err(&pdev->dev, "dpll_device_alloc failed\n");
@@ -4800,7 +4802,9 @@ ptp_ocp_probe(struct pci_dev *pdev, const struct pci_device_id *id)
goto out;
for (i = 0; i < OCP_SMA_NUM; i++) {
bp->sma[i].dpll_pin = dpll_pin_get(clkid, i, THIS_MODULE, &bp->sma[i].dpll_prop);
bp->sma[i].dpll_pin = dpll_pin_get(clkid, i, THIS_MODULE,
&bp->sma[i].dpll_prop,
&bp->sma[i].tracker);
if (IS_ERR(bp->sma[i].dpll_pin)) {
err = PTR_ERR(bp->sma[i].dpll_pin);
goto out_dpll;
@@ -4809,7 +4813,7 @@ ptp_ocp_probe(struct pci_dev *pdev, const struct pci_device_id *id)
err = dpll_pin_register(bp->dpll, bp->sma[i].dpll_pin, &dpll_pins_ops,
&bp->sma[i]);
if (err) {
dpll_pin_put(bp->sma[i].dpll_pin);
dpll_pin_put(bp->sma[i].dpll_pin, &bp->sma[i].tracker);
goto out_dpll;
}
}
@@ -4819,9 +4823,9 @@ ptp_ocp_probe(struct pci_dev *pdev, const struct pci_device_id *id)
out_dpll:
while (i--) {
dpll_pin_unregister(bp->dpll, bp->sma[i].dpll_pin, &dpll_pins_ops, &bp->sma[i]);
dpll_pin_put(bp->sma[i].dpll_pin);
dpll_pin_put(bp->sma[i].dpll_pin, &bp->sma[i].tracker);
}
dpll_device_put(bp->dpll);
dpll_device_put(bp->dpll, &bp->tracker);
out:
ptp_ocp_detach(bp);
out_disable:
@@ -4842,11 +4846,11 @@ ptp_ocp_remove(struct pci_dev *pdev)
for (i = 0; i < OCP_SMA_NUM; i++) {
if (bp->sma[i].dpll_pin) {
dpll_pin_unregister(bp->dpll, bp->sma[i].dpll_pin, &dpll_pins_ops, &bp->sma[i]);
dpll_pin_put(bp->sma[i].dpll_pin);
dpll_pin_put(bp->sma[i].dpll_pin, &bp->sma[i].tracker);
}
}
dpll_device_unregister(bp->dpll, &dpll_ops, bp);
dpll_device_put(bp->dpll);
dpll_device_put(bp->dpll, &bp->tracker);
devlink_unregister(devlink);
ptp_ocp_detach(bp);
pci_disable_device(pdev);
+55 -4
View File
@@ -11,11 +11,14 @@
#include <linux/device.h>
#include <linux/netlink.h>
#include <linux/netdevice.h>
#include <linux/notifier.h>
#include <linux/rtnetlink.h>
struct dpll_device;
struct dpll_pin;
struct dpll_pin_esync;
struct fwnode_handle;
struct ref_tracker;
struct dpll_device_ops {
int (*mode_get)(const struct dpll_device *dpll, void *dpll_priv,
@@ -171,6 +174,36 @@ struct dpll_pin_properties {
u32 phase_gran;
};
#ifdef CONFIG_DPLL_REFCNT_TRACKER
typedef struct ref_tracker *dpll_tracker;
#else
typedef struct {} dpll_tracker;
#endif
#define DPLL_DEVICE_CREATED 1
#define DPLL_DEVICE_DELETED 2
#define DPLL_DEVICE_CHANGED 3
#define DPLL_PIN_CREATED 4
#define DPLL_PIN_DELETED 5
#define DPLL_PIN_CHANGED 6
struct dpll_device_notifier_info {
struct dpll_device *dpll;
u32 id;
u32 idx;
u64 clock_id;
enum dpll_type type;
};
struct dpll_pin_notifier_info {
struct dpll_pin *pin;
u32 id;
u32 idx;
u64 clock_id;
const struct fwnode_handle *fwnode;
const struct dpll_pin_properties *prop;
};
#if IS_ENABLED(CONFIG_DPLL)
void dpll_netdev_pin_set(struct net_device *dev, struct dpll_pin *dpll_pin);
void dpll_netdev_pin_clear(struct net_device *dev);
@@ -178,6 +211,9 @@ void dpll_netdev_pin_clear(struct net_device *dev);
size_t dpll_netdev_pin_handle_size(const struct net_device *dev);
int dpll_netdev_add_pin_handle(struct sk_buff *msg,
const struct net_device *dev);
struct dpll_pin *fwnode_dpll_pin_find(struct fwnode_handle *fwnode,
dpll_tracker *tracker);
#else
static inline void
dpll_netdev_pin_set(struct net_device *dev, struct dpll_pin *dpll_pin) { }
@@ -193,12 +229,19 @@ dpll_netdev_add_pin_handle(struct sk_buff *msg, const struct net_device *dev)
{
return 0;
}
static inline struct dpll_pin *
fwnode_dpll_pin_find(struct fwnode_handle *fwnode, dpll_tracker *tracker)
{
return NULL;
}
#endif
struct dpll_device *
dpll_device_get(u64 clock_id, u32 dev_driver_id, struct module *module);
dpll_device_get(u64 clock_id, u32 dev_driver_id, struct module *module,
dpll_tracker *tracker);
void dpll_device_put(struct dpll_device *dpll);
void dpll_device_put(struct dpll_device *dpll, dpll_tracker *tracker);
int dpll_device_register(struct dpll_device *dpll, enum dpll_type type,
const struct dpll_device_ops *ops, void *priv);
@@ -206,9 +249,11 @@ int dpll_device_register(struct dpll_device *dpll, enum dpll_type type,
void dpll_device_unregister(struct dpll_device *dpll,
const struct dpll_device_ops *ops, void *priv);
#define DPLL_PIN_IDX_UNSPEC U32_MAX
struct dpll_pin *
dpll_pin_get(u64 clock_id, u32 dev_driver_id, struct module *module,
const struct dpll_pin_properties *prop);
const struct dpll_pin_properties *prop, dpll_tracker *tracker);
int dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin,
const struct dpll_pin_ops *ops, void *priv);
@@ -216,7 +261,9 @@ int dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin,
void dpll_pin_unregister(struct dpll_device *dpll, struct dpll_pin *pin,
const struct dpll_pin_ops *ops, void *priv);
void dpll_pin_put(struct dpll_pin *pin);
void dpll_pin_put(struct dpll_pin *pin, dpll_tracker *tracker);
void dpll_pin_fwnode_set(struct dpll_pin *pin, struct fwnode_handle *fwnode);
int dpll_pin_on_pin_register(struct dpll_pin *parent, struct dpll_pin *pin,
const struct dpll_pin_ops *ops, void *priv);
@@ -231,4 +278,8 @@ int dpll_device_change_ntf(struct dpll_device *dpll);
int dpll_pin_change_ntf(struct dpll_pin *pin);
int register_dpll_notifier(struct notifier_block *nb);
int unregister_dpll_notifier(struct notifier_block *nb);
#endif