Commit 651f7124 authored by Paolo Abeni's avatar Paolo Abeni
Browse files

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: default avatarPaolo Abeni <pabeni@redhat.com>
parents 0e6c95c9 ad1df4f2
Loading
Loading
Loading
Loading
+15 −0
Original line number Diff line number Diff line
@@ -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
Original line number Diff line number Diff line
@@ -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 @@ dpll_pin_alloc(u64 clock_id, u32 pin_idx, struct module *module,
	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 @@ int dpll_pin_on_pin_register(struct dpll_pin *parent, struct dpll_pin *pin,
					      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 −0
Original line number Diff line number Diff line
@@ -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 −0
Original line number Diff line number Diff line
@@ -761,17 +761,20 @@ dpll_device_event_send(enum dpll_cmd event, struct dpll_device *dpll)

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 @@ dpll_pin_event_send(enum dpll_cmd event, struct dpll_pin *pin)

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
Original line number Diff line number Diff line
@@ -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;
}

Loading