Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.5.6.
  1/*
  2 * Copyright (c) 2013, The Linux Foundation. All rights reserved.
  3 *
  4 * This software is licensed under the terms of the GNU General Public
  5 * License version 2, as published by the Free Software Foundation, and
  6 * may be copied, distributed, and modified under those terms.
  7 *
  8 * This program is distributed in the hope that it will be useful,
  9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 11 * GNU General Public License for more details.
 12 */
 13
 14#include <linux/kernel.h>
 15#include <linux/bitops.h>
 16#include <linux/err.h>
 17#include <linux/delay.h>
 18#include <linux/export.h>
 19#include <linux/clk-provider.h>
 20#include <linux/regmap.h>
 21
 22#include "clk-branch.h"
 23
 24static bool clk_branch_in_hwcg_mode(const struct clk_branch *br)
 25{
 26	u32 val;
 27
 28	if (!br->hwcg_reg)
 29		return 0;
 30
 31	regmap_read(br->clkr.regmap, br->hwcg_reg, &val);
 32
 33	return !!(val & BIT(br->hwcg_bit));
 34}
 35
 36static bool clk_branch_check_halt(const struct clk_branch *br, bool enabling)
 37{
 38	bool invert = (br->halt_check == BRANCH_HALT_ENABLE);
 39	u32 val;
 40
 41	regmap_read(br->clkr.regmap, br->halt_reg, &val);
 42
 43	val &= BIT(br->halt_bit);
 44	if (invert)
 45		val = !val;
 46
 47	return !!val == !enabling;
 48}
 49
 50#define BRANCH_CLK_OFF			BIT(31)
 51#define BRANCH_NOC_FSM_STATUS_SHIFT	28
 52#define BRANCH_NOC_FSM_STATUS_MASK	0x7
 53#define BRANCH_NOC_FSM_STATUS_ON	(0x2 << BRANCH_NOC_FSM_STATUS_SHIFT)
 54
 55static bool clk_branch2_check_halt(const struct clk_branch *br, bool enabling)
 56{
 57	u32 val;
 58	u32 mask;
 59
 60	mask = BRANCH_NOC_FSM_STATUS_MASK << BRANCH_NOC_FSM_STATUS_SHIFT;
 61	mask |= BRANCH_CLK_OFF;
 62
 63	regmap_read(br->clkr.regmap, br->halt_reg, &val);
 64
 65	if (enabling) {
 66		val &= mask;
 67		return (val & BRANCH_CLK_OFF) == 0 ||
 68			val == BRANCH_NOC_FSM_STATUS_ON;
 69	} else {
 70		return val & BRANCH_CLK_OFF;
 71	}
 72}
 73
 74static int clk_branch_wait(const struct clk_branch *br, bool enabling,
 75		bool (check_halt)(const struct clk_branch *, bool))
 76{
 77	bool voted = br->halt_check & BRANCH_VOTED;
 78	const char *name = clk_hw_get_name(&br->clkr.hw);
 79
 80	/* Skip checking halt bit if the clock is in hardware gated mode */
 81	if (clk_branch_in_hwcg_mode(br))
 82		return 0;
 83
 84	if (br->halt_check == BRANCH_HALT_DELAY || (!enabling && voted)) {
 85		udelay(10);
 86	} else if (br->halt_check == BRANCH_HALT_ENABLE ||
 87		   br->halt_check == BRANCH_HALT ||
 88		   (enabling && voted)) {
 89		int count = 200;
 90
 91		while (count-- > 0) {
 92			if (check_halt(br, enabling))
 93				return 0;
 94			udelay(1);
 95		}
 96		WARN(1, "%s status stuck at 'o%s'", name,
 97				enabling ? "ff" : "n");
 98		return -EBUSY;
 99	}
100	return 0;
101}
102
103static int clk_branch_toggle(struct clk_hw *hw, bool en,
104		bool (check_halt)(const struct clk_branch *, bool))
105{
106	struct clk_branch *br = to_clk_branch(hw);
107	int ret;
108
109	if (en) {
110		ret = clk_enable_regmap(hw);
111		if (ret)
112			return ret;
113	} else {
114		clk_disable_regmap(hw);
115	}
116
117	return clk_branch_wait(br, en, check_halt);
118}
119
120static int clk_branch_enable(struct clk_hw *hw)
121{
122	return clk_branch_toggle(hw, true, clk_branch_check_halt);
123}
124
125static void clk_branch_disable(struct clk_hw *hw)
126{
127	clk_branch_toggle(hw, false, clk_branch_check_halt);
128}
129
130const struct clk_ops clk_branch_ops = {
131	.enable = clk_branch_enable,
132	.disable = clk_branch_disable,
133	.is_enabled = clk_is_enabled_regmap,
134};
135EXPORT_SYMBOL_GPL(clk_branch_ops);
136
137static int clk_branch2_enable(struct clk_hw *hw)
138{
139	return clk_branch_toggle(hw, true, clk_branch2_check_halt);
140}
141
142static void clk_branch2_disable(struct clk_hw *hw)
143{
144	clk_branch_toggle(hw, false, clk_branch2_check_halt);
145}
146
147const struct clk_ops clk_branch2_ops = {
148	.enable = clk_branch2_enable,
149	.disable = clk_branch2_disable,
150	.is_enabled = clk_is_enabled_regmap,
151};
152EXPORT_SYMBOL_GPL(clk_branch2_ops);
153
154const struct clk_ops clk_branch_simple_ops = {
155	.enable = clk_enable_regmap,
156	.disable = clk_disable_regmap,
157	.is_enabled = clk_is_enabled_regmap,
158};
159EXPORT_SYMBOL_GPL(clk_branch_simple_ops);