Linux Audio

Check our new training course

Loading...
Note: File does not exist in v5.14.15.
  1/*
  2 * Copyright (c) 2015 Endless Mobile, Inc.
  3 * Author: Carlo Caione <carlo@endlessm.com>
  4 *
  5 * This program is free software; you can redistribute it and/or modify it
  6 * under the terms and conditions of the GNU General Public License,
  7 * version 2, as published by the Free Software Foundation.
  8 *
  9 * This program is distributed in the hope it will be useful, but WITHOUT
 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 12 * more details.
 13 *
 14 * You should have received a copy of the GNU General Public License along with
 15 * this program.  If not, see <http://www.gnu.org/licenses/>.
 16 */
 17
 18#include <linux/clk-provider.h>
 19#include <linux/mfd/syscon.h>
 20#include <linux/slab.h>
 21
 22#include "clkc.h"
 23
 24static DEFINE_SPINLOCK(clk_lock);
 25
 26static struct clk **clks;
 27static struct clk_onecell_data clk_data;
 28
 29struct clk ** __init meson_clk_init(struct device_node *np,
 30				   unsigned long nr_clks)
 31{
 32	clks = kcalloc(nr_clks, sizeof(*clks), GFP_KERNEL);
 33	if (!clks)
 34		return ERR_PTR(-ENOMEM);
 35
 36	clk_data.clks = clks;
 37	clk_data.clk_num = nr_clks;
 38	of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
 39
 40	return clks;
 41}
 42
 43static void meson_clk_add_lookup(struct clk *clk, unsigned int id)
 44{
 45	if (clks && id)
 46		clks[id] = clk;
 47}
 48
 49static struct clk * __init
 50meson_clk_register_composite(const struct clk_conf *clk_conf,
 51			     void __iomem *clk_base)
 52{
 53	struct clk *clk;
 54	struct clk_mux *mux = NULL;
 55	struct clk_divider *div = NULL;
 56	struct clk_gate *gate = NULL;
 57	const struct clk_ops *mux_ops = NULL;
 58	const struct composite_conf *composite_conf;
 59
 60	composite_conf = clk_conf->conf.composite;
 61
 62	if (clk_conf->num_parents > 1) {
 63		mux = kzalloc(sizeof(*mux), GFP_KERNEL);
 64		if (!mux)
 65			return ERR_PTR(-ENOMEM);
 66
 67		mux->reg = clk_base + clk_conf->reg_off
 68				+ composite_conf->mux_parm.reg_off;
 69		mux->shift = composite_conf->mux_parm.shift;
 70		mux->mask = BIT(composite_conf->mux_parm.width) - 1;
 71		mux->flags = composite_conf->mux_flags;
 72		mux->lock = &clk_lock;
 73		mux->table = composite_conf->mux_table;
 74		mux_ops = (composite_conf->mux_flags & CLK_MUX_READ_ONLY) ?
 75			  &clk_mux_ro_ops : &clk_mux_ops;
 76	}
 77
 78	if (MESON_PARM_APPLICABLE(&composite_conf->div_parm)) {
 79		div = kzalloc(sizeof(*div), GFP_KERNEL);
 80		if (!div) {
 81			clk = ERR_PTR(-ENOMEM);
 82			goto error;
 83		}
 84
 85		div->reg = clk_base + clk_conf->reg_off
 86				+ composite_conf->div_parm.reg_off;
 87		div->shift = composite_conf->div_parm.shift;
 88		div->width = composite_conf->div_parm.width;
 89		div->lock = &clk_lock;
 90		div->flags = composite_conf->div_flags;
 91		div->table = composite_conf->div_table;
 92	}
 93
 94	if (MESON_PARM_APPLICABLE(&composite_conf->gate_parm)) {
 95		gate = kzalloc(sizeof(*gate), GFP_KERNEL);
 96		if (!gate) {
 97			clk = ERR_PTR(-ENOMEM);
 98			goto error;
 99		}
100
101		gate->reg = clk_base + clk_conf->reg_off
102				+ composite_conf->div_parm.reg_off;
103		gate->bit_idx = composite_conf->gate_parm.shift;
104		gate->flags = composite_conf->gate_flags;
105		gate->lock = &clk_lock;
106	}
107
108	clk = clk_register_composite(NULL, clk_conf->clk_name,
109				    clk_conf->clks_parent,
110				    clk_conf->num_parents,
111				    mux ? &mux->hw : NULL, mux_ops,
112				    div ? &div->hw : NULL, &clk_divider_ops,
113				    gate ? &gate->hw : NULL, &clk_gate_ops,
114				    clk_conf->flags);
115	if (IS_ERR(clk))
116		goto error;
117
118	return clk;
119
120error:
121	kfree(gate);
122	kfree(div);
123	kfree(mux);
124
125	return clk;
126}
127
128static struct clk * __init
129meson_clk_register_fixed_factor(const struct clk_conf *clk_conf,
130				void __iomem *clk_base)
131{
132	struct clk *clk;
133	const struct fixed_fact_conf *fixed_fact_conf;
134	const struct parm *p;
135	unsigned int mult, div;
136	u32 reg;
137
138	fixed_fact_conf = &clk_conf->conf.fixed_fact;
139
140	mult = clk_conf->conf.fixed_fact.mult;
141	div = clk_conf->conf.fixed_fact.div;
142
143	if (!mult) {
144		mult = 1;
145		p = &fixed_fact_conf->mult_parm;
146		if (MESON_PARM_APPLICABLE(p)) {
147			reg = readl(clk_base + clk_conf->reg_off + p->reg_off);
148			mult = PARM_GET(p->width, p->shift, reg);
149		}
150	}
151
152	if (!div) {
153		div = 1;
154		p = &fixed_fact_conf->div_parm;
155		if (MESON_PARM_APPLICABLE(p)) {
156			reg = readl(clk_base + clk_conf->reg_off + p->reg_off);
157			mult = PARM_GET(p->width, p->shift, reg);
158		}
159	}
160
161	clk = clk_register_fixed_factor(NULL,
162			clk_conf->clk_name,
163			clk_conf->clks_parent[0],
164			clk_conf->flags,
165			mult, div);
166
167	return clk;
168}
169
170static struct clk * __init
171meson_clk_register_fixed_rate(const struct clk_conf *clk_conf,
172			      void __iomem *clk_base)
173{
174	struct clk *clk;
175	const struct fixed_rate_conf *fixed_rate_conf;
176	const struct parm *r;
177	unsigned long rate;
178	u32 reg;
179
180	fixed_rate_conf = &clk_conf->conf.fixed_rate;
181	rate = fixed_rate_conf->rate;
182
183	if (!rate) {
184		r = &fixed_rate_conf->rate_parm;
185		reg = readl(clk_base + clk_conf->reg_off + r->reg_off);
186		rate = PARM_GET(r->width, r->shift, reg);
187	}
188
189	rate *= 1000000;
190
191	clk = clk_register_fixed_rate(NULL,
192			clk_conf->clk_name,
193			clk_conf->num_parents
194				? clk_conf->clks_parent[0] : NULL,
195			clk_conf->flags, rate);
196
197	return clk;
198}
199
200void __init meson_clk_register_clks(const struct clk_conf *clk_confs,
201				    unsigned int nr_confs,
202				    void __iomem *clk_base)
203{
204	unsigned int i;
205	struct clk *clk = NULL;
206
207	for (i = 0; i < nr_confs; i++) {
208		const struct clk_conf *clk_conf = &clk_confs[i];
209
210		switch (clk_conf->clk_type) {
211		case CLK_FIXED_RATE:
212			clk = meson_clk_register_fixed_rate(clk_conf,
213							    clk_base);
214			break;
215		case CLK_FIXED_FACTOR:
216			clk = meson_clk_register_fixed_factor(clk_conf,
217							      clk_base);
218			break;
219		case CLK_COMPOSITE:
220			clk = meson_clk_register_composite(clk_conf,
221							   clk_base);
222			break;
223		case CLK_CPU:
224			clk = meson_clk_register_cpu(clk_conf, clk_base,
225						     &clk_lock);
226			break;
227		case CLK_PLL:
228			clk = meson_clk_register_pll(clk_conf, clk_base,
229						     &clk_lock);
230			break;
231		default:
232			clk = NULL;
233		}
234
235		if (!clk) {
236			pr_err("%s: unknown clock type %d\n", __func__,
237			       clk_conf->clk_type);
238			continue;
239		}
240
241		if (IS_ERR(clk)) {
242			pr_warn("%s: Unable to create %s clock\n", __func__,
243				clk_conf->clk_name);
244			continue;
245		}
246
247		meson_clk_add_lookup(clk, clk_conf->clk_id);
248	}
249}