1/* SPDX-License-Identifier: GPL-2.0 */
2#ifndef _CCU_MUX_H_
3#define _CCU_MUX_H_
4
5#include <linux/clk-provider.h>
6
7#include "ccu_common.h"
8
9struct ccu_mux_fixed_prediv {
10	u8	index;
11	u16	div;
12};
13
14struct ccu_mux_var_prediv {
15	u8	index;
16	u8	shift;
17	u8	width;
18};
19
20struct ccu_mux_internal {
21	u8		shift;
22	u8		width;
23	const u8	*table;
24
25	const struct ccu_mux_fixed_prediv	*fixed_predivs;
26	u8		n_predivs;
27
28	const struct ccu_mux_var_prediv		*var_predivs;
29	u8		n_var_predivs;
30};
31
32#define _SUNXI_CCU_MUX_TABLE(_shift, _width, _table)	\
33	{						\
34		.shift	= _shift,			\
35		.width	= _width,			\
36		.table	= _table,			\
37	}
38
39#define _SUNXI_CCU_MUX(_shift, _width) \
40	_SUNXI_CCU_MUX_TABLE(_shift, _width, NULL)
41
42struct ccu_mux {
43	u32			enable;
44
45	struct ccu_mux_internal	mux;
46	struct ccu_common	common;
47};
48
49#define SUNXI_CCU_MUX_TABLE_WITH_GATE_FEAT(_struct, _name, _parents, _table,	\
50				     _reg, _shift, _width, _gate,		\
51				     _flags, _features)				\
52	struct ccu_mux _struct = {						\
53		.enable	= _gate,						\
54		.mux	= _SUNXI_CCU_MUX_TABLE(_shift, _width, _table),		\
55		.common	= {							\
56			.reg		= _reg,					\
57			.hw.init	= CLK_HW_INIT_PARENTS(_name,		\
58							      _parents,		\
59							      &ccu_mux_ops,	\
60							      _flags),		\
61			.features	= _features,				\
62		}								\
63	}
64
65#define SUNXI_CCU_MUX_TABLE_WITH_GATE_CLOSEST(_struct, _name, _parents,	\
66					      _table, _reg, _shift,	\
67					      _width, _gate, _flags)	\
68	SUNXI_CCU_MUX_TABLE_WITH_GATE_FEAT(_struct, _name, _parents,	\
69					   _table, _reg, _shift,	\
70					   _width, _gate, _flags,	\
71					   CCU_FEATURE_CLOSEST_RATE)
72
73#define SUNXI_CCU_MUX_TABLE_WITH_GATE(_struct, _name, _parents, _table,	\
74				     _reg, _shift, _width, _gate,	\
75				     _flags)				\
76	SUNXI_CCU_MUX_TABLE_WITH_GATE_FEAT(_struct, _name, _parents,	\
77					   _table, _reg, _shift,	\
78					   _width, _gate, _flags, 0)
79
80#define SUNXI_CCU_MUX_WITH_GATE(_struct, _name, _parents, _reg,		\
81				_shift, _width, _gate, _flags)		\
82	SUNXI_CCU_MUX_TABLE_WITH_GATE(_struct, _name, _parents, NULL,	\
83				      _reg, _shift, _width, _gate,	\
84				      _flags)
85
86#define SUNXI_CCU_MUX(_struct, _name, _parents, _reg, _shift, _width,	\
87		      _flags)						\
88	SUNXI_CCU_MUX_TABLE_WITH_GATE(_struct, _name, _parents, NULL,	\
89				      _reg, _shift, _width, 0, _flags)
90
91#define SUNXI_CCU_MUX_DATA_WITH_GATE(_struct, _name, _parents, _reg,	\
92				     _shift, _width, _gate, _flags)	\
93	struct ccu_mux _struct = {					\
94		.enable	= _gate,					\
95		.mux	= _SUNXI_CCU_MUX(_shift, _width),		\
96		.common	= {						\
97			.reg		= _reg,				\
98			.hw.init	= CLK_HW_INIT_PARENTS_DATA(_name, \
99								   _parents, \
100								   &ccu_mux_ops, \
101								   _flags), \
102		}							\
103	}
104
105#define SUNXI_CCU_MUX_DATA(_struct, _name, _parents, _reg,		\
106		      _shift, _width, _flags)				\
107	SUNXI_CCU_MUX_DATA_WITH_GATE(_struct, _name, _parents, _reg,	\
108				     _shift, _width, 0, _flags)
109
110#define SUNXI_CCU_MUX_HW_WITH_GATE(_struct, _name, _parents, _reg,	\
111				   _shift, _width, _gate, _flags)	\
112	struct ccu_mux _struct = {					\
113		.enable	= _gate,					\
114		.mux	= _SUNXI_CCU_MUX(_shift, _width),		\
115		.common	= {						\
116			.reg		= _reg,				\
117			.hw.init	= CLK_HW_INIT_PARENTS_HW(_name, \
118								 _parents, \
119								 &ccu_mux_ops, \
120								 _flags), \
121		}							\
122	}
123
124static inline struct ccu_mux *hw_to_ccu_mux(struct clk_hw *hw)
125{
126	struct ccu_common *common = hw_to_ccu_common(hw);
127
128	return container_of(common, struct ccu_mux, common);
129}
130
131extern const struct clk_ops ccu_mux_ops;
132
133unsigned long ccu_mux_helper_apply_prediv(struct ccu_common *common,
134					  struct ccu_mux_internal *cm,
135					  int parent_index,
136					  unsigned long parent_rate);
137int ccu_mux_helper_determine_rate(struct ccu_common *common,
138				  struct ccu_mux_internal *cm,
139				  struct clk_rate_request *req,
140				  unsigned long (*round)(struct ccu_mux_internal *,
141							 struct clk_hw *,
142							 unsigned long *,
143							 unsigned long,
144							 void *),
145				  void *data);
146u8 ccu_mux_helper_get_parent(struct ccu_common *common,
147			     struct ccu_mux_internal *cm);
148int ccu_mux_helper_set_parent(struct ccu_common *common,
149			      struct ccu_mux_internal *cm,
150			      u8 index);
151
152struct ccu_mux_nb {
153	struct notifier_block	clk_nb;
154	struct ccu_common	*common;
155	struct ccu_mux_internal	*cm;
156
157	u32	delay_us;	/* How many us to wait after reparenting */
158	u8	bypass_index;	/* Which parent to temporarily use */
159	u8	original_index;	/* This is set by the notifier callback */
160};
161
162#define to_ccu_mux_nb(_nb) container_of(_nb, struct ccu_mux_nb, clk_nb)
163
164int ccu_mux_notifier_register(struct clk *clk, struct ccu_mux_nb *mux_nb);
165
166#endif /* _CCU_MUX_H_ */
167