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

Commit 2f8da4e8 authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "clk: qcom: Introduce rate change notifiers"

parents 06c0f8cc 67d8ff5f
Loading
Loading
Loading
Loading
+170 −3
Original line number Diff line number Diff line
@@ -43,6 +43,7 @@ static LIST_HEAD(handoff_vdd_list);

static DEFINE_MUTEX(msm_clock_init_lock);
LIST_HEAD(orphan_clk_list);
static LIST_HEAD(clk_notifier_list);

/* Find the voltage level required for a given rate. */
int find_vdd_level(struct clk *clk, unsigned long rate)
@@ -474,6 +475,163 @@ int clk_reset(struct clk *clk, enum clk_reset_action action)
}
EXPORT_SYMBOL(clk_reset);

/**
 * __clk_notify - call clk notifier chain
 * @clk: struct clk * that is changing rate
 * @msg: clk notifier type (see include/linux/clk.h)
 * @old_rate: old clk rate
 * @new_rate: new clk rate
 *
 * Triggers a notifier call chain on the clk rate-change notification
 * for 'clk'.  Passes a pointer to the struct clk and the previous
 * and current rates to the notifier callback.  Intended to be called by
 * internal clock code only.  Returns NOTIFY_DONE from the last driver
 * called if all went well, or NOTIFY_STOP or NOTIFY_BAD immediately if
 * a driver returns that.
 */
static int __clk_notify(struct clk *clk, unsigned long msg,
		unsigned long old_rate, unsigned long new_rate)
{
	struct msm_clk_notifier *cn;
	struct msm_clk_notifier_data cnd;
	int ret = NOTIFY_DONE;

	cnd.clk = clk;
	cnd.old_rate = old_rate;
	cnd.new_rate = new_rate;

	list_for_each_entry(cn, &clk_notifier_list, node) {
		if (cn->clk == clk) {
			ret = srcu_notifier_call_chain(&cn->notifier_head, msg,
					&cnd);
			break;
		}
	}

	return ret;
}

/*
 * clk rate change notifiers
 *
 * Note - The following notifier functionality is a verbatim copy
 * of the implementation in the common clock framework, copied here
 * until MSM switches to the common clock framework.
 */

/**
 * msm_clk_notif_register - add a clk rate change notifier
 * @clk: struct clk * to watch
 * @nb: struct notifier_block * with callback info
 *
 * Request notification when clk's rate changes.  This uses an SRCU
 * notifier because we want it to block and notifier unregistrations are
 * uncommon.  The callbacks associated with the notifier must not
 * re-enter into the clk framework by calling any top-level clk APIs;
 * this will cause a nested prepare_lock mutex.
 *
 * Pre-change notifier callbacks will be passed the current, pre-change
 * rate of the clk via struct msm_clk_notifier_data.old_rate.  The new,
 * post-change rate of the clk is passed via struct
 * msm_clk_notifier_data.new_rate.
 *
 * Post-change notifiers will pass the now-current, post-change rate of
 * the clk in both struct msm_clk_notifier_data.old_rate and struct
 * msm_clk_notifier_data.new_rate.
 *
 * Abort-change notifiers are effectively the opposite of pre-change
 * notifiers: the original pre-change clk rate is passed in via struct
 * msm_clk_notifier_data.new_rate and the failed post-change rate is passed
 * in via struct msm_clk_notifier_data.old_rate.
 *
 * msm_clk_notif_register() must be called from non-atomic context.
 * Returns -EINVAL if called with null arguments, -ENOMEM upon
 * allocation failure; otherwise, passes along the return value of
 * srcu_notifier_chain_register().
 */
int msm_clk_notif_register(struct clk *clk, struct notifier_block *nb)
{
	struct msm_clk_notifier *cn;
	int ret = -ENOMEM;

	if (!clk || !nb)
		return -EINVAL;

	mutex_lock(&clk->prepare_lock);

	/* search the list of notifiers for this clk */
	list_for_each_entry(cn, &clk_notifier_list, node)
		if (cn->clk == clk)
			break;

	/* if clk wasn't in the notifier list, allocate new clk_notifier */
	if (cn->clk != clk) {
		cn = kzalloc(sizeof(struct msm_clk_notifier), GFP_KERNEL);
		if (!cn)
			goto out;

		cn->clk = clk;
		srcu_init_notifier_head(&cn->notifier_head);

		list_add(&cn->node, &clk_notifier_list);
	}

	ret = srcu_notifier_chain_register(&cn->notifier_head, nb);

	clk->notifier_count++;

out:
	mutex_unlock(&clk->prepare_lock);

	return ret;
}

/**
 * msm_clk_notif_unregister - remove a clk rate change notifier
 * @clk: struct clk *
 * @nb: struct notifier_block * with callback info
 *
 * Request no further notification for changes to 'clk' and frees memory
 * allocated in msm_clk_notifier_register.
 *
 * Returns -EINVAL if called with null arguments; otherwise, passes
 * along the return value of srcu_notifier_chain_unregister().
 */
int msm_clk_notif_unregister(struct clk *clk, struct notifier_block *nb)
{
	struct msm_clk_notifier *cn = NULL;
	int ret = -EINVAL;

	if (!clk || !nb)
		return -EINVAL;

	mutex_lock(&clk->prepare_lock);

	list_for_each_entry(cn, &clk_notifier_list, node)
		if (cn->clk == clk)
			break;

	if (cn->clk == clk) {
		ret = srcu_notifier_chain_unregister(&cn->notifier_head, nb);

		clk->notifier_count--;

		/* XXX the notifier code should handle this better */
		if (!cn->notifier_head.head) {
			srcu_cleanup_notifier_head(&cn->notifier_head);
			list_del(&cn->node);
			kfree(cn);
		}

	} else {
		ret = -ENOENT;
	}

	mutex_unlock(&clk->prepare_lock);

	return ret;
}

unsigned long clk_get_rate(struct clk *clk)
{
	if (IS_ERR_OR_NULL(clk))
@@ -514,10 +672,14 @@ int clk_set_rate(struct clk *clk, unsigned long rate)

	start_rate = clk->rate;

	if (clk->ops->pre_set_rate)
	if (clk->notifier_count)
		__clk_notify(clk, PRE_RATE_CHANGE, clk->rate, rate);

	if (clk->ops->pre_set_rate) {
		rc = clk->ops->pre_set_rate(clk, rate);
		if (rc)
		goto out;
			goto abort_set_rate;
	}

	/* Enforce vdd requirements for target frequency. */
	if (clk->prepare_count) {
@@ -538,10 +700,15 @@ int clk_set_rate(struct clk *clk, unsigned long rate)
	if (clk->ops->post_set_rate)
		clk->ops->post_set_rate(clk, start_rate);

	if (clk->notifier_count)
		__clk_notify(clk, POST_RATE_CHANGE, start_rate, clk->rate);

out:
	mutex_unlock(&clk->prepare_lock);
	return rc;

abort_set_rate:
	__clk_notify(clk, ABORT_RATE_CHANGE, clk->rate, rate);
err_set_rate:
	if (clk->prepare_count)
		unvote_rate_vdd(clk, rate);
+1 −0
Original line number Diff line number Diff line
@@ -185,6 +185,7 @@ struct clk {
	struct list_head list;

	unsigned count;
	unsigned notifier_count;
	spinlock_t lock;
	unsigned prepare_count;
	struct mutex prepare_lock;
+63 −1
Original line number Diff line number Diff line
/* Copyright (c) 2009, 2012-2013 The Linux Foundation. All rights reserved.
/* Copyright (c) 2009, 2012-2014 The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
@@ -12,6 +12,8 @@
#ifndef __MACH_CLK_H
#define __MACH_CLK_H

#include <linux/notifier.h>

#define CLKFLAG_INVERT			0x00000001
#define CLKFLAG_NOINVERT		0x00000002
#define CLKFLAG_NONEST			0x00000004
@@ -57,4 +59,64 @@ int parent_to_src_sel(struct clk_src *parents, int num_parents, struct clk *p);
/* returns the mux selection index associated with a particular parent */
int clk_get_parent_sel(struct clk *c, struct clk *parent);

/**
 * DOC: clk notifier callback types
 *
 * PRE_RATE_CHANGE - called immediately before the clk rate is changed,
 *     to indicate that the rate change will proceed.  Drivers must
 *     immediately terminate any operations that will be affected by the
 *     rate change.  Callbacks may either return NOTIFY_DONE, NOTIFY_OK,
 *     NOTIFY_STOP or NOTIFY_BAD.
 *
 * ABORT_RATE_CHANGE: called if the rate change failed for some reason
 *     after PRE_RATE_CHANGE.  In this case, all registered notifiers on
 *     the clk will be called with ABORT_RATE_CHANGE. Callbacks must
 *     always return NOTIFY_DONE or NOTIFY_OK.
 *
 * POST_RATE_CHANGE - called after the clk rate change has successfully
 *     completed.  Callbacks must always return NOTIFY_DONE or NOTIFY_OK.
 *
 */
#define PRE_RATE_CHANGE			BIT(0)
#define POST_RATE_CHANGE		BIT(1)
#define ABORT_RATE_CHANGE		BIT(2)

/**
 * struct msm_clk_notifier - associate a clk with a notifier
 * @clk: struct clk * to associate the notifier with
 * @notifier_head: a blocking_notifier_head for this clk
 * @node: linked list pointers
 *
 * A list of struct clk_notifier is maintained by the notifier code.
 * An entry is created whenever code registers the first notifier on a
 * particular @clk.  Future notifiers on that @clk are added to the
 * @notifier_head.
 */
struct msm_clk_notifier {
	struct clk			*clk;
	struct srcu_notifier_head	notifier_head;
	struct list_head		node;
};

/**
 * struct msm_clk_notifier_data - rate data to pass to the notifier callback
 * @clk: struct clk * being changed
 * @old_rate: previous rate of this clk
 * @new_rate: new rate of this clk
 *
 * For a pre-notifier, old_rate is the clk's rate before this rate
 * change, and new_rate is what the rate will be in the future.  For a
 * post-notifier, old_rate and new_rate are both set to the clk's
 * current rate (this was done to optimize the implementation).
 */
struct msm_clk_notifier_data {
	struct clk		*clk;
	unsigned long		old_rate;
	unsigned long		new_rate;
};

int msm_clk_notif_register(struct clk *clk, struct notifier_block *nb);

int msm_clk_notif_unregister(struct clk *clk, struct notifier_block *nb);

#endif