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/*
  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);