Linux Audio

Check our new training course

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