Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit cc6ef92a authored by Michael Turquette's avatar Michael Turquette Committed by Gerrit - the friendly Code Review server
Browse files

clk: introduce CLK_ENABLE_HAND_OFF flag



Some clocks are critical to system operation (e.g. cpu, memory, etc) and
should not be gated until a driver that knows best claims such a clock
and expressly gates that clock through the normal clk.h api.

The typical way to handle this is for the clk driver or some other early
code to call clk_prepare_enable on this important clock as soon as it is
registered and before the clk_disable_unused garbage collector kicks in.

This patch introduces a formal way to handle this scenario that is
provided by the clk framework. Clk driver authors can set the
CLK_ENABLE_HAND_OFF flag in their clk data, which will cause the clk to
be enabled in clk_register(). Then when the first clk consumer driver
comes along and calls clk_get() & clk_prepare_enable(), the reference
counts taken during clk registration are transferred (or handed off) to
the clk consumer.

At this point handling the clk is the same as any other clock which as
not set the new CLK_ENABLE_HAND_OFF flag. In fact no changes to any
clock consumer driver are needed for this to work.

Change-Id: Ib5247f6bceb1f555c03103f061af089755b2de62
Signed-off-by: default avatarMichael Turquette <mturquette@baylibre.com>
Patch-mainline: patchwork.kernel.org @ 02/11/16, 9:19
Signed-off-by: default avatarTaniya Das <tdas@codeaurora.org>
Signed-off-by: default avatarDavid Dai <daidavid1@codeaurora.org>
parent 96aaf694
Loading
Loading
Loading
Loading
+72 −0
Original line number Diff line number Diff line
@@ -74,6 +74,8 @@ struct clk_core {
	unsigned int		enable_count;
	unsigned int		prepare_count;
	unsigned int		protect_count;
	bool			need_handoff_enable;
	bool			need_handoff_prepare;
	unsigned long		min_rate;
	unsigned long		max_rate;
	unsigned long		accuracy;
@@ -1078,6 +1080,19 @@ int clk_prepare(struct clk *clk)
	if (!clk)
		return 0;

	/*
	 * setting CLK_ENABLE_HAND_OFF flag triggers this conditional
	 *
	 * need_handoff_prepare implies this clk was already prepared by
	 * __clk_init. now we have a proper user, so unset the flag in our
	 * internal bookkeeping. See CLK_ENABLE_HAND_OFF flag in clk-provider.h
	 * for details.
	 */
	if (clk->core->need_handoff_prepare) {
		clk->core->need_handoff_prepare = false;
		return 0;
	}

	return clk_core_prepare_lock(clk->core);
}
EXPORT_SYMBOL_GPL(clk_prepare);
@@ -1205,6 +1220,19 @@ int clk_enable(struct clk *clk)
	if (!clk)
		return 0;

	/*
	 * setting CLK_ENABLE_HAND_OFF flag triggers this conditional
	 *
	 * need_handoff_enable implies this clk was already enabled by
	 * __clk_init. now we have a proper user, so unset the flag in our
	 * internal bookkeeping. See CLK_ENABLE_HAND_OFF flag in clk-provider.h
	 * for details.
	 */
	if (clk->core->need_handoff_enable) {
		clk->core->need_handoff_enable = false;
		return 0;
	}

	return clk_core_enable_lock(clk->core);
}
EXPORT_SYMBOL_GPL(clk_enable);
@@ -3423,6 +3451,50 @@ static int __clk_core_init(struct clk_core *core)
		}
	}

	/*
	 * optional platform-specific magic
	 *
	 * The .init callback is not used by any of the basic clock types, but
	 * exists for weird hardware that must perform initialization magic.
	 * Please consider other ways of solving initialization problems before
	 * using this callback, as its use is discouraged.
	 */
	if (core->ops->init)
		core->ops->init(core->hw);

	if (core->flags & CLK_IS_CRITICAL) {
		unsigned long flags;

		clk_core_prepare(core);

		flags = clk_enable_lock();
		clk_core_enable(core);
		clk_enable_unlock(flags);
	}

	/*
	 * enable clocks with the CLK_ENABLE_HAND_OFF flag set
	 *
	 * This flag causes the framework to enable the clock at registration
	 * time, which is sometimes necessary for clocks that would cause a
	 * system crash when gated (e.g. cpu, memory, etc). The prepare_count
	 * is migrated over to the first clk consumer to call clk_prepare().
	 * Similarly the clk's enable_count is migrated to the first consumer
	 * to call clk_enable().
	 */
	if (core->flags & CLK_ENABLE_HAND_OFF) {
		unsigned long flags;

		core->need_handoff_prepare = true;
		core->need_handoff_enable = true;
		ret = clk_core_prepare(core);
		if (ret)
			goto out;
		flags = clk_enable_lock();
		clk_core_enable(core);
		clk_enable_unlock(flags);
	}

	kref_init(&core->ref);
out:
	clk_pm_runtime_put(core);
+5 −0
Original line number Diff line number Diff line
@@ -41,6 +41,11 @@
#define CLK_OPS_PARENT_ENABLE	BIT(12)
/* duty cycle call may be forwarded to the parent clock */
#define CLK_DUTY_CYCLE_PARENT	BIT(13)
#define CLK_ENABLE_HAND_OFF	BIT(14) /* enable clock when registered. */
					/*
					 * hand-off enable_count & prepare_count
					 * to first consumer that enables clk
					 */

struct clk;
struct clk_hw;