162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci//
362306a36Sopenharmony_ci// Spreadtrum gate clock driver
462306a36Sopenharmony_ci//
562306a36Sopenharmony_ci// Copyright (C) 2017 Spreadtrum, Inc.
662306a36Sopenharmony_ci// Author: Chunyan Zhang <chunyan.zhang@spreadtrum.com>
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/clk-provider.h>
962306a36Sopenharmony_ci#include <linux/regmap.h>
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include "gate.h"
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_cistatic void clk_gate_toggle(const struct sprd_gate *sg, bool en)
1462306a36Sopenharmony_ci{
1562306a36Sopenharmony_ci	const struct sprd_clk_common *common = &sg->common;
1662306a36Sopenharmony_ci	unsigned int reg;
1762306a36Sopenharmony_ci	bool set = sg->flags & CLK_GATE_SET_TO_DISABLE ? true : false;
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci	set ^= en;
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci	regmap_read(common->regmap, common->reg, &reg);
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci	if (set)
2462306a36Sopenharmony_ci		reg |= sg->enable_mask;
2562306a36Sopenharmony_ci	else
2662306a36Sopenharmony_ci		reg &= ~sg->enable_mask;
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci	regmap_write(common->regmap, common->reg, reg);
2962306a36Sopenharmony_ci}
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_cistatic void clk_sc_gate_toggle(const struct sprd_gate *sg, bool en)
3262306a36Sopenharmony_ci{
3362306a36Sopenharmony_ci	const struct sprd_clk_common *common = &sg->common;
3462306a36Sopenharmony_ci	bool set = sg->flags & CLK_GATE_SET_TO_DISABLE ? 1 : 0;
3562306a36Sopenharmony_ci	unsigned int offset;
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	set ^= en;
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	/*
4062306a36Sopenharmony_ci	 * Each set/clear gate clock has three registers:
4162306a36Sopenharmony_ci	 * common->reg			- base register
4262306a36Sopenharmony_ci	 * common->reg + offset		- set register
4362306a36Sopenharmony_ci	 * common->reg + 2 * offset	- clear register
4462306a36Sopenharmony_ci	 */
4562306a36Sopenharmony_ci	offset = set ? sg->sc_offset : sg->sc_offset * 2;
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	regmap_write(common->regmap, common->reg + offset,
4862306a36Sopenharmony_ci			  sg->enable_mask);
4962306a36Sopenharmony_ci}
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_cistatic void sprd_gate_disable(struct clk_hw *hw)
5262306a36Sopenharmony_ci{
5362306a36Sopenharmony_ci	struct sprd_gate *sg = hw_to_sprd_gate(hw);
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	clk_gate_toggle(sg, false);
5662306a36Sopenharmony_ci}
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_cistatic int sprd_gate_enable(struct clk_hw *hw)
5962306a36Sopenharmony_ci{
6062306a36Sopenharmony_ci	struct sprd_gate *sg = hw_to_sprd_gate(hw);
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	clk_gate_toggle(sg, true);
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	return 0;
6562306a36Sopenharmony_ci}
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_cistatic void sprd_sc_gate_disable(struct clk_hw *hw)
6862306a36Sopenharmony_ci{
6962306a36Sopenharmony_ci	struct sprd_gate *sg = hw_to_sprd_gate(hw);
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	clk_sc_gate_toggle(sg, false);
7262306a36Sopenharmony_ci}
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_cistatic int sprd_sc_gate_enable(struct clk_hw *hw)
7562306a36Sopenharmony_ci{
7662306a36Sopenharmony_ci	struct sprd_gate *sg = hw_to_sprd_gate(hw);
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	clk_sc_gate_toggle(sg, true);
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	return 0;
8162306a36Sopenharmony_ci}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_cistatic int sprd_pll_sc_gate_prepare(struct clk_hw *hw)
8462306a36Sopenharmony_ci{
8562306a36Sopenharmony_ci	struct sprd_gate *sg = hw_to_sprd_gate(hw);
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	clk_sc_gate_toggle(sg, true);
8862306a36Sopenharmony_ci	udelay(sg->udelay);
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	return 0;
9162306a36Sopenharmony_ci}
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_cistatic int sprd_gate_is_enabled(struct clk_hw *hw)
9462306a36Sopenharmony_ci{
9562306a36Sopenharmony_ci	struct sprd_gate *sg = hw_to_sprd_gate(hw);
9662306a36Sopenharmony_ci	struct sprd_clk_common *common = &sg->common;
9762306a36Sopenharmony_ci	struct clk_hw *parent;
9862306a36Sopenharmony_ci	unsigned int reg;
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	if (sg->flags & SPRD_GATE_NON_AON) {
10162306a36Sopenharmony_ci		parent = clk_hw_get_parent(hw);
10262306a36Sopenharmony_ci		if (!parent || !clk_hw_is_enabled(parent))
10362306a36Sopenharmony_ci			return 0;
10462306a36Sopenharmony_ci	}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	regmap_read(common->regmap, common->reg, &reg);
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	if (sg->flags & CLK_GATE_SET_TO_DISABLE)
10962306a36Sopenharmony_ci		reg ^= sg->enable_mask;
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	reg &= sg->enable_mask;
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	return reg ? 1 : 0;
11462306a36Sopenharmony_ci}
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ciconst struct clk_ops sprd_gate_ops = {
11762306a36Sopenharmony_ci	.disable	= sprd_gate_disable,
11862306a36Sopenharmony_ci	.enable		= sprd_gate_enable,
11962306a36Sopenharmony_ci	.is_enabled	= sprd_gate_is_enabled,
12062306a36Sopenharmony_ci};
12162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sprd_gate_ops);
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ciconst struct clk_ops sprd_sc_gate_ops = {
12462306a36Sopenharmony_ci	.disable	= sprd_sc_gate_disable,
12562306a36Sopenharmony_ci	.enable		= sprd_sc_gate_enable,
12662306a36Sopenharmony_ci	.is_enabled	= sprd_gate_is_enabled,
12762306a36Sopenharmony_ci};
12862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sprd_sc_gate_ops);
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ciconst struct clk_ops sprd_pll_sc_gate_ops = {
13162306a36Sopenharmony_ci	.unprepare	= sprd_sc_gate_disable,
13262306a36Sopenharmony_ci	.prepare	= sprd_pll_sc_gate_prepare,
13362306a36Sopenharmony_ci	.is_enabled	= sprd_gate_is_enabled,
13462306a36Sopenharmony_ci};
13562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sprd_pll_sc_gate_ops);
136