1/* SPDX-License-Identifier: GPL-2.0-only */
2/*
3 * Copyright (c) 2016 Maxime Ripard. All rights reserved.
4 */
5
6#ifndef _CCU_DIV_H_
7#define _CCU_DIV_H_
8
9#include <linux/clk-provider.h>
10
11#include "ccu_common.h"
12#include "ccu_mux.h"
13
14/**
15 * struct ccu_div_internal - Internal divider description
16 * @shift: Bit offset of the divider in its register
17 * @width: Width of the divider field in its register
18 * @max: Maximum value allowed for that divider. This is the
19 *       arithmetic value, not the maximum value to be set in the
20 *       register.
21 * @flags: clk_divider flags to apply on this divider
22 * @table: Divider table pointer (if applicable)
23 *
24 * That structure represents a single divider, and is meant to be
25 * embedded in other structures representing the various clock
26 * classes.
27 *
28 * It is basically a wrapper around the clk_divider functions
29 * arguments.
30 */
31struct ccu_div_internal {
32	u8			shift;
33	u8			width;
34
35	u32			max;
36	u32			offset;
37
38	u32			flags;
39
40	struct clk_div_table	*table;
41};
42
43#define _SUNXI_CCU_DIV_TABLE_FLAGS(_shift, _width, _table, _flags)	\
44	{								\
45		.shift	= _shift,					\
46		.width	= _width,					\
47		.flags	= _flags,					\
48		.table	= _table,					\
49	}
50
51#define _SUNXI_CCU_DIV_TABLE(_shift, _width, _table)			\
52	_SUNXI_CCU_DIV_TABLE_FLAGS(_shift, _width, _table, 0)
53
54#define _SUNXI_CCU_DIV_OFFSET_MAX_FLAGS(_shift, _width, _off, _max, _flags) \
55	{								\
56		.shift	= _shift,					\
57		.width	= _width,					\
58		.flags	= _flags,					\
59		.max	= _max,						\
60		.offset	= _off,						\
61	}
62
63#define _SUNXI_CCU_DIV_MAX_FLAGS(_shift, _width, _max, _flags)		\
64	_SUNXI_CCU_DIV_OFFSET_MAX_FLAGS(_shift, _width, 1, _max, _flags)
65
66#define _SUNXI_CCU_DIV_FLAGS(_shift, _width, _flags)			\
67	_SUNXI_CCU_DIV_MAX_FLAGS(_shift, _width, 0, _flags)
68
69#define _SUNXI_CCU_DIV_MAX(_shift, _width, _max)			\
70	_SUNXI_CCU_DIV_MAX_FLAGS(_shift, _width, _max, 0)
71
72#define _SUNXI_CCU_DIV_OFFSET(_shift, _width, _offset)			\
73	_SUNXI_CCU_DIV_OFFSET_MAX_FLAGS(_shift, _width, _offset, 0, 0)
74
75#define _SUNXI_CCU_DIV(_shift, _width)					\
76	_SUNXI_CCU_DIV_FLAGS(_shift, _width, 0)
77
78struct ccu_div {
79	u32			enable;
80
81	struct ccu_div_internal	div;
82	struct ccu_mux_internal	mux;
83	struct ccu_common	common;
84	unsigned int		fixed_post_div;
85};
86
87#define SUNXI_CCU_DIV_TABLE_WITH_GATE(_struct, _name, _parent, _reg,	\
88				      _shift, _width,			\
89				      _table, _gate, _flags)		\
90	struct ccu_div _struct = {					\
91		.div		= _SUNXI_CCU_DIV_TABLE(_shift, _width,	\
92						       _table),		\
93		.enable		= _gate,				\
94		.common	= {						\
95			.reg		= _reg,				\
96			.hw.init	= CLK_HW_INIT(_name,		\
97						      _parent,		\
98						      &ccu_div_ops,	\
99						      _flags),		\
100		}							\
101	}
102
103
104#define SUNXI_CCU_DIV_TABLE(_struct, _name, _parent, _reg,		\
105			    _shift, _width,				\
106			    _table, _flags)				\
107	SUNXI_CCU_DIV_TABLE_WITH_GATE(_struct, _name, _parent, _reg,	\
108				      _shift, _width, _table, 0,	\
109				      _flags)
110
111#define SUNXI_CCU_DIV_TABLE_HW(_struct, _name, _parent, _reg,		\
112			       _shift, _width,				\
113			       _table, _flags)				\
114	struct ccu_div _struct = {					\
115		.div		= _SUNXI_CCU_DIV_TABLE(_shift, _width,	\
116						       _table),		\
117		.common	= {						\
118			.reg		= _reg,				\
119			.hw.init	= CLK_HW_INIT_HW(_name,		\
120							 _parent,	\
121							 &ccu_div_ops,	\
122							 _flags),	\
123		}							\
124	}
125
126
127#define SUNXI_CCU_M_WITH_MUX_TABLE_GATE(_struct, _name,			\
128					_parents, _table,		\
129					_reg,				\
130					_mshift, _mwidth,		\
131					_muxshift, _muxwidth,		\
132					_gate, _flags)			\
133	struct ccu_div _struct = {					\
134		.enable	= _gate,					\
135		.div	= _SUNXI_CCU_DIV(_mshift, _mwidth),		\
136		.mux	= _SUNXI_CCU_MUX_TABLE(_muxshift, _muxwidth, _table), \
137		.common	= {						\
138			.reg		= _reg,				\
139			.hw.init	= CLK_HW_INIT_PARENTS(_name,	\
140							      _parents, \
141							      &ccu_div_ops, \
142							      _flags),	\
143		},							\
144	}
145
146#define SUNXI_CCU_M_WITH_MUX_TABLE_GATE_CLOSEST(_struct, _name,		\
147						_parents, _table,	\
148						_reg,			\
149						_mshift, _mwidth,	\
150						_muxshift, _muxwidth,	\
151						_gate, _flags)		\
152	struct ccu_div _struct = {					\
153		.enable	= _gate,					\
154		.div	= _SUNXI_CCU_DIV_FLAGS(_mshift, _mwidth, CLK_DIVIDER_ROUND_CLOSEST), \
155		.mux	= _SUNXI_CCU_MUX_TABLE(_muxshift, _muxwidth, _table), \
156		.common	= {						\
157			.reg		= _reg,				\
158			.hw.init	= CLK_HW_INIT_PARENTS(_name,	\
159							      _parents, \
160							      &ccu_div_ops, \
161							      _flags),	\
162			.features	= CCU_FEATURE_CLOSEST_RATE,	\
163		},							\
164	}
165
166#define SUNXI_CCU_M_WITH_MUX_GATE(_struct, _name, _parents, _reg,	\
167				  _mshift, _mwidth, _muxshift, _muxwidth, \
168				  _gate, _flags)			\
169	SUNXI_CCU_M_WITH_MUX_TABLE_GATE(_struct, _name,			\
170					_parents, NULL,			\
171					_reg, _mshift, _mwidth,		\
172					_muxshift, _muxwidth,		\
173					_gate, _flags)
174
175#define SUNXI_CCU_M_WITH_MUX_GATE_CLOSEST(_struct, _name, _parents,	\
176					  _reg, _mshift, _mwidth,	\
177					  _muxshift, _muxwidth,		\
178					  _gate, _flags)		\
179	SUNXI_CCU_M_WITH_MUX_TABLE_GATE_CLOSEST(_struct, _name,		\
180						_parents, NULL,		\
181						_reg, _mshift, _mwidth,	\
182						_muxshift, _muxwidth,	\
183						_gate, _flags)
184
185#define SUNXI_CCU_M_WITH_MUX(_struct, _name, _parents, _reg,		\
186			     _mshift, _mwidth, _muxshift, _muxwidth,	\
187			     _flags)					\
188	SUNXI_CCU_M_WITH_MUX_TABLE_GATE(_struct, _name,			\
189					_parents, NULL,			\
190					_reg, _mshift, _mwidth,		\
191					_muxshift, _muxwidth,		\
192					0, _flags)
193
194
195#define SUNXI_CCU_M_WITH_GATE(_struct, _name, _parent, _reg,		\
196			      _mshift, _mwidth,	_gate,			\
197			      _flags)					\
198	struct ccu_div _struct = {					\
199		.enable	= _gate,					\
200		.div	= _SUNXI_CCU_DIV(_mshift, _mwidth),		\
201		.common	= {						\
202			.reg		= _reg,				\
203			.hw.init	= CLK_HW_INIT(_name,		\
204						      _parent,		\
205						      &ccu_div_ops,	\
206						      _flags),		\
207		},							\
208	}
209
210#define SUNXI_CCU_M(_struct, _name, _parent, _reg, _mshift, _mwidth,	\
211		    _flags)						\
212	SUNXI_CCU_M_WITH_GATE(_struct, _name, _parent, _reg,		\
213			      _mshift, _mwidth, 0, _flags)
214
215#define SUNXI_CCU_M_DATA_WITH_MUX_GATE(_struct, _name, _parents, _reg,	\
216				       _mshift, _mwidth,		\
217				       _muxshift, _muxwidth,		\
218				       _gate, _flags)			\
219	struct ccu_div _struct = {					\
220		.enable	= _gate,					\
221		.div	= _SUNXI_CCU_DIV(_mshift, _mwidth),		\
222		.mux	= _SUNXI_CCU_MUX(_muxshift, _muxwidth),		\
223		.common	= {						\
224			.reg		= _reg,				\
225			.hw.init	= CLK_HW_INIT_PARENTS_DATA(_name, \
226								   _parents, \
227								   &ccu_div_ops, \
228								   _flags), \
229		},							\
230	}
231
232#define SUNXI_CCU_M_DATA_WITH_MUX(_struct, _name, _parents, _reg,	\
233				  _mshift, _mwidth,			\
234				  _muxshift, _muxwidth,			\
235				  _flags)				\
236	SUNXI_CCU_M_DATA_WITH_MUX_GATE(_struct, _name, _parents, _reg,  \
237				       _mshift, _mwidth,		\
238				       _muxshift, _muxwidth,		\
239				       0, _flags)
240
241#define SUNXI_CCU_M_HW_WITH_MUX_GATE(_struct, _name, _parents, _reg,	\
242				     _mshift, _mwidth, _muxshift, _muxwidth, \
243				     _gate, _flags)			\
244	struct ccu_div _struct = {					\
245		.enable	= _gate,					\
246		.div	= _SUNXI_CCU_DIV(_mshift, _mwidth),		\
247		.mux	= _SUNXI_CCU_MUX(_muxshift, _muxwidth),		\
248		.common	= {						\
249			.reg		= _reg,				\
250			.hw.init	= CLK_HW_INIT_PARENTS_HW(_name,	\
251								 _parents, \
252								 &ccu_div_ops, \
253								 _flags), \
254		},							\
255	}
256
257#define SUNXI_CCU_M_HWS_WITH_GATE(_struct, _name, _parent, _reg,	\
258				  _mshift, _mwidth, _gate,		\
259				  _flags)				\
260	struct ccu_div _struct = {					\
261		.enable	= _gate,					\
262		.div	= _SUNXI_CCU_DIV(_mshift, _mwidth),		\
263		.common	= {						\
264			.reg		= _reg,				\
265			.hw.init	= CLK_HW_INIT_HWS(_name,	\
266							  _parent,	\
267							  &ccu_div_ops,	\
268							  _flags),	\
269		},							\
270	}
271
272#define SUNXI_CCU_M_HWS(_struct, _name, _parent, _reg, _mshift,		\
273			_mwidth, _flags)				\
274	SUNXI_CCU_M_HWS_WITH_GATE(_struct, _name, _parent, _reg,	\
275				  _mshift, _mwidth, 0, _flags)
276
277static inline struct ccu_div *hw_to_ccu_div(struct clk_hw *hw)
278{
279	struct ccu_common *common = hw_to_ccu_common(hw);
280
281	return container_of(common, struct ccu_div, common);
282}
283
284extern const struct clk_ops ccu_div_ops;
285
286#endif /* _CCU_DIV_H_ */
287