1// SPDX-License-Identifier: GPL-2.0
2//
3// Spreadtrum gate clock driver
4//
5// Copyright (C) 2017 Spreadtrum, Inc.
6// Author: Chunyan Zhang <chunyan.zhang@spreadtrum.com>
7
8#include <linux/clk-provider.h>
9#include <linux/regmap.h>
10
11#include "gate.h"
12
13static void clk_gate_toggle(const struct sprd_gate *sg, bool en)
14{
15	const struct sprd_clk_common *common = &sg->common;
16	unsigned int reg;
17	bool set = sg->flags & CLK_GATE_SET_TO_DISABLE ? true : false;
18
19	set ^= en;
20
21	regmap_read(common->regmap, common->reg, &reg);
22
23	if (set)
24		reg |= sg->enable_mask;
25	else
26		reg &= ~sg->enable_mask;
27
28	regmap_write(common->regmap, common->reg, reg);
29}
30
31static void clk_sc_gate_toggle(const struct sprd_gate *sg, bool en)
32{
33	const struct sprd_clk_common *common = &sg->common;
34	bool set = sg->flags & CLK_GATE_SET_TO_DISABLE ? 1 : 0;
35	unsigned int offset;
36
37	set ^= en;
38
39	/*
40	 * Each set/clear gate clock has three registers:
41	 * common->reg			- base register
42	 * common->reg + offset		- set register
43	 * common->reg + 2 * offset	- clear register
44	 */
45	offset = set ? sg->sc_offset : sg->sc_offset * 2;
46
47	regmap_write(common->regmap, common->reg + offset,
48			  sg->enable_mask);
49}
50
51static void sprd_gate_disable(struct clk_hw *hw)
52{
53	struct sprd_gate *sg = hw_to_sprd_gate(hw);
54
55	clk_gate_toggle(sg, false);
56}
57
58static int sprd_gate_enable(struct clk_hw *hw)
59{
60	struct sprd_gate *sg = hw_to_sprd_gate(hw);
61
62	clk_gate_toggle(sg, true);
63
64	return 0;
65}
66
67static void sprd_sc_gate_disable(struct clk_hw *hw)
68{
69	struct sprd_gate *sg = hw_to_sprd_gate(hw);
70
71	clk_sc_gate_toggle(sg, false);
72}
73
74static int sprd_sc_gate_enable(struct clk_hw *hw)
75{
76	struct sprd_gate *sg = hw_to_sprd_gate(hw);
77
78	clk_sc_gate_toggle(sg, true);
79
80	return 0;
81}
82
83static int sprd_pll_sc_gate_prepare(struct clk_hw *hw)
84{
85	struct sprd_gate *sg = hw_to_sprd_gate(hw);
86
87	clk_sc_gate_toggle(sg, true);
88	udelay(sg->udelay);
89
90	return 0;
91}
92
93static int sprd_gate_is_enabled(struct clk_hw *hw)
94{
95	struct sprd_gate *sg = hw_to_sprd_gate(hw);
96	struct sprd_clk_common *common = &sg->common;
97	struct clk_hw *parent;
98	unsigned int reg;
99
100	if (sg->flags & SPRD_GATE_NON_AON) {
101		parent = clk_hw_get_parent(hw);
102		if (!parent || !clk_hw_is_enabled(parent))
103			return 0;
104	}
105
106	regmap_read(common->regmap, common->reg, &reg);
107
108	if (sg->flags & CLK_GATE_SET_TO_DISABLE)
109		reg ^= sg->enable_mask;
110
111	reg &= sg->enable_mask;
112
113	return reg ? 1 : 0;
114}
115
116const struct clk_ops sprd_gate_ops = {
117	.disable	= sprd_gate_disable,
118	.enable		= sprd_gate_enable,
119	.is_enabled	= sprd_gate_is_enabled,
120};
121EXPORT_SYMBOL_GPL(sprd_gate_ops);
122
123const struct clk_ops sprd_sc_gate_ops = {
124	.disable	= sprd_sc_gate_disable,
125	.enable		= sprd_sc_gate_enable,
126	.is_enabled	= sprd_gate_is_enabled,
127};
128EXPORT_SYMBOL_GPL(sprd_sc_gate_ops);
129
130const struct clk_ops sprd_pll_sc_gate_ops = {
131	.unprepare	= sprd_sc_gate_disable,
132	.prepare	= sprd_pll_sc_gate_prepare,
133	.is_enabled	= sprd_gate_is_enabled,
134};
135EXPORT_SYMBOL_GPL(sprd_pll_sc_gate_ops);
136