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

Commit 8516d6c2 authored by Deepak Katragadda's avatar Deepak Katragadda Committed by Jeevan Shriram
Browse files

clk: msm: clock-local2: Add support for enabling clock HW_CTL



Add a new hw_ctl_clk type to allow clock clients to enable
hardware dynamic gating of the clock branch.
Clients should use the clk_enable API on a separate hw_ctl_clk
clock structure to set this bit. Vice-versa for clearing it.
It is mandatory that the clients call clk_enable on the actual
branch clock before enabling the hw_ctl_clk clock.

CRs-Fixed: 1012355
Change-Id: I24e78353fa07f537bafc322dba6b1ffac913cd1d
Signed-off-by: default avatarDeepak Katragadda <dkatraga@codeaurora.org>
parent c3496782
Loading
Loading
Loading
Loading
+97 −1
Original line number Diff line number Diff line
@@ -763,9 +763,18 @@ static int branch_clk_set_rate(struct clk *c, unsigned long rate)
{
	struct branch_clk *clkh, *branch = to_branch_clk(c);
	struct clk *clkp, *parent = c->parent;
	unsigned long curr_rate, new_rate, other_rate = 0;
	unsigned long flags, curr_rate, new_rate, other_rate = 0;
	int ret = 0;

	spin_lock_irqsave(&local_clock_reg_lock, flags);
	if (readl_relaxed(CBCR_REG(branch)) & CBCR_HW_CTL_BIT) {
		pr_err("Cannot scale %s clock while HW gating is enabled. Use corresponding hw_ctl_clk to scale it\n",
				c->dbg_name);
		spin_unlock_irqrestore(&local_clock_reg_lock, flags);
		return -EINVAL;
	}
	spin_unlock_irqrestore(&local_clock_reg_lock, flags);

	if (branch->max_div)
		return branch_cdiv_set_rate(branch, rate);

@@ -940,6 +949,85 @@ static void __iomem *branch_clk_list_registers(struct clk *c, int n,
	return CBCR_REG(branch);
}

static void _hw_ctl_clk_enable(struct hw_ctl_clk *hwctl_clk)
{
	unsigned long flags;
	u32 cbcr_val;

	spin_lock_irqsave(&local_clock_reg_lock, flags);
	cbcr_val = readl_relaxed(CBCR_REG(hwctl_clk));
	cbcr_val |= CBCR_HW_CTL_BIT;
	writel_relaxed(cbcr_val, CBCR_REG(hwctl_clk));
	spin_unlock_irqrestore(&local_clock_reg_lock, flags);
}

static int hw_ctl_clk_enable(struct clk *c)
{
	struct hw_ctl_clk *hwctl_clk = to_hw_ctl_clk(c);
	struct clk *parent = c->parent;

	/* The parent branch clock should have been prepared prior to this. */
	if (!parent || (parent && !parent->prepare_count))
		return -EINVAL;

	_hw_ctl_clk_enable(hwctl_clk);
	return 0;
}

static void _hw_ctl_clk_disable(struct hw_ctl_clk *hwctl_clk)
{
	unsigned long flags;
	u32 cbcr_val;

	spin_lock_irqsave(&local_clock_reg_lock, flags);
	cbcr_val = readl_relaxed(CBCR_REG(hwctl_clk));
	cbcr_val &= ~CBCR_HW_CTL_BIT;
	writel_relaxed(cbcr_val, CBCR_REG(hwctl_clk));
	spin_unlock_irqrestore(&local_clock_reg_lock, flags);
}

static void hw_ctl_clk_disable(struct clk *c)
{
	struct hw_ctl_clk *hwctl_clk = to_hw_ctl_clk(c);

	if (!c->parent)
		return;

	_hw_ctl_clk_disable(hwctl_clk);
}

static int hw_ctl_clk_set_rate(struct clk *c, unsigned long rate)
{
	struct hw_ctl_clk *hwctl_clk = to_hw_ctl_clk(c);
	struct clk *parent = c->parent;
	int ret = 0;

	if (!parent)
		return -EINVAL;
	/*
	 * Switch back to SW control while doing a frequency change to avoid
	 * having the downstream clock being gated at the same time that the
	 * RCG rate switch happens.
	 */
	_hw_ctl_clk_disable(hwctl_clk);
	ret = clk_set_rate(parent, rate);
	if (ret)
		return ret;
	_hw_ctl_clk_enable(hwctl_clk);

	return 0;
}

static long hw_ctl_clk_round_rate(struct clk *c, unsigned long rate)
{
	return clk_round_rate(c->parent, rate);
}

static unsigned long hw_ctl_clk_get_rate(struct clk *c)
{
	return clk_get_rate(c->parent);
}

/*
 * Voteable clock functions
 */
@@ -2131,6 +2219,14 @@ struct clk_ops clk_ops_branch = {
	.list_registers = branch_clk_list_registers,
};

struct clk_ops clk_ops_branch_hw_ctl = {
	.enable = hw_ctl_clk_enable,
	.disable = hw_ctl_clk_disable,
	.set_rate = hw_ctl_clk_set_rate,
	.get_rate = hw_ctl_clk_get_rate,
	.round_rate = hw_ctl_clk_round_rate,
};

struct clk_ops clk_ops_vote = {
	.enable = local_vote_clk_enable,
	.disable = local_vote_clk_disable,
+18 −0
Original line number Diff line number Diff line
@@ -128,6 +128,23 @@ static inline struct branch_clk *to_branch_clk(struct clk *clk)
	return container_of(clk, struct branch_clk, c);
}

/**
 * struct hw_ctl_clk - Clock structure to enable/disable dynamic clock gating
 * @c: clk
 * @cbcr_reg: branch control register
 * @base: pointer to base address of ioremapped registers.
 */
struct hw_ctl_clk {
	struct clk c;
	u32 cbcr_reg;
	void __iomem *const *base;
};

static inline struct hw_ctl_clk *to_hw_ctl_clk(struct clk *clk)
{
	return container_of(clk, struct hw_ctl_clk, c);
}

/**
 * struct local_vote_clk - Voteable branch clock
 * @c: clk
@@ -234,6 +251,7 @@ extern struct clk_ops clk_ops_empty;
extern struct clk_ops clk_ops_rcg;
extern struct clk_ops clk_ops_rcg_mnd;
extern struct clk_ops clk_ops_branch;
extern struct clk_ops clk_ops_branch_hw_ctl;
extern struct clk_ops clk_ops_vote;
extern struct clk_ops clk_ops_rcg_hdmi;
extern struct clk_ops clk_ops_rcg_edp;