File Index
Files and directories
Sign in
FILE

clk-phase.c

File preview with syntax highlighting.

linux-6.19.14/drivers/clk/meson/clk-phase.c C
  1// SPDX-License-Identifier: (GPL-2.0 OR MIT)
  2/*
  3 * Copyright (c) 2018 BayLibre, SAS.
  4 * Author: Jerome Brunet <jbrunet@baylibre.com>
  5 */
  6
  7#include <linux/clk-provider.h>
  8#include <linux/module.h>
  9
 10#include "clk-regmap.h"
 11#include "clk-phase.h"
 12
 13#define phase_step(_width) (360 / (1 << (_width)))
 14
 15static inline struct meson_clk_phase_data *
 16meson_clk_phase_data(struct clk_regmap *clk)
 17{
 18	return (struct meson_clk_phase_data *)clk->data;
 19}
 20
 21static int meson_clk_degrees_from_val(unsigned int val, unsigned int width)
 22{
 23	return phase_step(width) * val;
 24}
 25
 26static unsigned int meson_clk_degrees_to_val(int degrees, unsigned int width)
 27{
 28	unsigned int val = DIV_ROUND_CLOSEST(degrees, phase_step(width));
 29
 30	/*
 31	 * This last calculation is here for cases when degrees is rounded
 32	 * to 360, in which case val == (1 << width).
 33	 */
 34	return val % (1 << width);
 35}
 36
 37static int meson_clk_phase_get_phase(struct clk_hw *hw)
 38{
 39	struct clk_regmap *clk = to_clk_regmap(hw);
 40	struct meson_clk_phase_data *phase = meson_clk_phase_data(clk);
 41	unsigned int val;
 42
 43	val = meson_parm_read(clk->map, &phase->ph);
 44
 45	return meson_clk_degrees_from_val(val, phase->ph.width);
 46}
 47
 48static int meson_clk_phase_set_phase(struct clk_hw *hw, int degrees)
 49{
 50	struct clk_regmap *clk = to_clk_regmap(hw);
 51	struct meson_clk_phase_data *phase = meson_clk_phase_data(clk);
 52	unsigned int val;
 53
 54	val = meson_clk_degrees_to_val(degrees, phase->ph.width);
 55	meson_parm_write(clk->map, &phase->ph, val);
 56
 57	return 0;
 58}
 59
 60const struct clk_ops meson_clk_phase_ops = {
 61	.init		= clk_regmap_init,
 62	.get_phase	= meson_clk_phase_get_phase,
 63	.set_phase	= meson_clk_phase_set_phase,
 64};
 65EXPORT_SYMBOL_NS_GPL(meson_clk_phase_ops, "CLK_MESON");
 66
 67/*
 68 * This is a special clock for the audio controller.
 69 * The phase of mst_sclk clock output can be controlled independently
 70 * for the outside world (ph0), the tdmout (ph1) and tdmin (ph2).
 71 * Controlling these 3 phases as just one makes things simpler and
 72 * give the same clock view to all the element on the i2s bus.
 73 * If necessary, we can still control the phase in the tdm block
 74 * which makes these independent control redundant.
 75 */
 76static inline struct meson_clk_triphase_data *
 77meson_clk_triphase_data(struct clk_regmap *clk)
 78{
 79	return (struct meson_clk_triphase_data *)clk->data;
 80}
 81
 82static int meson_clk_triphase_sync(struct clk_hw *hw)
 83{
 84	struct clk_regmap *clk = to_clk_regmap(hw);
 85	struct meson_clk_triphase_data *tph = meson_clk_triphase_data(clk);
 86	unsigned int val;
 87	int ret;
 88
 89	ret = clk_regmap_init(hw);
 90	if (ret)
 91		return ret;
 92
 93	/* Get phase 0 and sync it to phase 1 and 2 */
 94	val = meson_parm_read(clk->map, &tph->ph0);
 95	meson_parm_write(clk->map, &tph->ph1, val);
 96	meson_parm_write(clk->map, &tph->ph2, val);
 97
 98	return 0;
 99}
100
101static int meson_clk_triphase_get_phase(struct clk_hw *hw)
102{
103	struct clk_regmap *clk = to_clk_regmap(hw);
104	struct meson_clk_triphase_data *tph = meson_clk_triphase_data(clk);
105	unsigned int val;
106
107	/* Phase are in sync, reading phase 0 is enough */
108	val = meson_parm_read(clk->map, &tph->ph0);
109
110	return meson_clk_degrees_from_val(val, tph->ph0.width);
111}
112
113static int meson_clk_triphase_set_phase(struct clk_hw *hw, int degrees)
114{
115	struct clk_regmap *clk = to_clk_regmap(hw);
116	struct meson_clk_triphase_data *tph = meson_clk_triphase_data(clk);
117	unsigned int val;
118
119	val = meson_clk_degrees_to_val(degrees, tph->ph0.width);
120	meson_parm_write(clk->map, &tph->ph0, val);
121	meson_parm_write(clk->map, &tph->ph1, val);
122	meson_parm_write(clk->map, &tph->ph2, val);
123
124	return 0;
125}
126
127const struct clk_ops meson_clk_triphase_ops = {
128	.init		= meson_clk_triphase_sync,
129	.get_phase	= meson_clk_triphase_get_phase,
130	.set_phase	= meson_clk_triphase_set_phase,
131};
132EXPORT_SYMBOL_NS_GPL(meson_clk_triphase_ops, "CLK_MESON");
133
134/*
135 * This is a special clock for the audio controller.
136 * This drive a bit clock inverter for which the
137 * opposite value of the inverter bit needs to be manually
138 * set into another bit
139 */
140static inline struct meson_sclk_ws_inv_data *
141meson_sclk_ws_inv_data(struct clk_regmap *clk)
142{
143	return (struct meson_sclk_ws_inv_data *)clk->data;
144}
145
146static int meson_sclk_ws_inv_sync(struct clk_hw *hw)
147{
148	struct clk_regmap *clk = to_clk_regmap(hw);
149	struct meson_sclk_ws_inv_data *tph = meson_sclk_ws_inv_data(clk);
150	unsigned int val;
151	int ret;
152
153	ret = clk_regmap_init(hw);
154	if (ret)
155		return ret;
156
157	/* Get phase and sync the inverted value to ws */
158	val = meson_parm_read(clk->map, &tph->ph);
159	meson_parm_write(clk->map, &tph->ws, val ? 0 : 1);
160
161	return 0;
162}
163
164static int meson_sclk_ws_inv_get_phase(struct clk_hw *hw)
165{
166	struct clk_regmap *clk = to_clk_regmap(hw);
167	struct meson_sclk_ws_inv_data *tph = meson_sclk_ws_inv_data(clk);
168	unsigned int val;
169
170	val = meson_parm_read(clk->map, &tph->ph);
171
172	return meson_clk_degrees_from_val(val, tph->ph.width);
173}
174
175static int meson_sclk_ws_inv_set_phase(struct clk_hw *hw, int degrees)
176{
177	struct clk_regmap *clk = to_clk_regmap(hw);
178	struct meson_sclk_ws_inv_data *tph = meson_sclk_ws_inv_data(clk);
179	unsigned int val;
180
181	val = meson_clk_degrees_to_val(degrees, tph->ph.width);
182	meson_parm_write(clk->map, &tph->ph, val);
183	meson_parm_write(clk->map, &tph->ws, val ? 0 : 1);
184	return 0;
185}
186
187const struct clk_ops meson_sclk_ws_inv_ops = {
188	.init		= meson_sclk_ws_inv_sync,
189	.get_phase	= meson_sclk_ws_inv_get_phase,
190	.set_phase	= meson_sclk_ws_inv_set_phase,
191};
192EXPORT_SYMBOL_NS_GPL(meson_sclk_ws_inv_ops, "CLK_MESON");
193
194MODULE_DESCRIPTION("Amlogic phase driver");
195MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
196MODULE_LICENSE("GPL");
197MODULE_IMPORT_NS("CLK_MESON");
~/ Read-only file preview.