18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * clkgen-mux.c: ST GEN-MUX Clock driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2014 STMicroelectronics (R&D) Limited
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Authors: Stephen Gallimore <stephen.gallimore@st.com>
88c2ecf20Sopenharmony_ci *	    Pankaj Dev <pankaj.dev@st.com>
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/slab.h>
128c2ecf20Sopenharmony_ci#include <linux/io.h>
138c2ecf20Sopenharmony_ci#include <linux/of_address.h>
148c2ecf20Sopenharmony_ci#include <linux/clk.h>
158c2ecf20Sopenharmony_ci#include <linux/clk-provider.h>
168c2ecf20Sopenharmony_ci#include "clkgen.h"
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_cistatic const char ** __init clkgen_mux_get_parents(struct device_node *np,
198c2ecf20Sopenharmony_ci						       int *num_parents)
208c2ecf20Sopenharmony_ci{
218c2ecf20Sopenharmony_ci	const char **parents;
228c2ecf20Sopenharmony_ci	unsigned int nparents;
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci	nparents = of_clk_get_parent_count(np);
258c2ecf20Sopenharmony_ci	if (WARN_ON(!nparents))
268c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci	parents = kcalloc(nparents, sizeof(const char *), GFP_KERNEL);
298c2ecf20Sopenharmony_ci	if (!parents)
308c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci	*num_parents = of_clk_parent_fill(np, parents, nparents);
338c2ecf20Sopenharmony_ci	return parents;
348c2ecf20Sopenharmony_ci}
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_cistruct clkgen_mux_data {
378c2ecf20Sopenharmony_ci	u32 offset;
388c2ecf20Sopenharmony_ci	u8 shift;
398c2ecf20Sopenharmony_ci	u8 width;
408c2ecf20Sopenharmony_ci	spinlock_t *lock;
418c2ecf20Sopenharmony_ci	unsigned long clk_flags;
428c2ecf20Sopenharmony_ci	u8 mux_flags;
438c2ecf20Sopenharmony_ci};
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_cistatic struct clkgen_mux_data stih407_a9_mux_data = {
468c2ecf20Sopenharmony_ci	.offset = 0x1a4,
478c2ecf20Sopenharmony_ci	.shift = 0,
488c2ecf20Sopenharmony_ci	.width = 2,
498c2ecf20Sopenharmony_ci	.lock = &clkgen_a9_lock,
508c2ecf20Sopenharmony_ci};
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_cistatic void __init st_of_clkgen_mux_setup(struct device_node *np,
538c2ecf20Sopenharmony_ci		struct clkgen_mux_data *data)
548c2ecf20Sopenharmony_ci{
558c2ecf20Sopenharmony_ci	struct clk *clk;
568c2ecf20Sopenharmony_ci	void __iomem *reg;
578c2ecf20Sopenharmony_ci	const char **parents;
588c2ecf20Sopenharmony_ci	int num_parents = 0;
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	reg = of_iomap(np, 0);
618c2ecf20Sopenharmony_ci	if (!reg) {
628c2ecf20Sopenharmony_ci		pr_err("%s: Failed to get base address\n", __func__);
638c2ecf20Sopenharmony_ci		return;
648c2ecf20Sopenharmony_ci	}
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	parents = clkgen_mux_get_parents(np, &num_parents);
678c2ecf20Sopenharmony_ci	if (IS_ERR(parents)) {
688c2ecf20Sopenharmony_ci		pr_err("%s: Failed to get parents (%ld)\n",
698c2ecf20Sopenharmony_ci				__func__, PTR_ERR(parents));
708c2ecf20Sopenharmony_ci		goto err_parents;
718c2ecf20Sopenharmony_ci	}
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	clk = clk_register_mux(NULL, np->name, parents, num_parents,
748c2ecf20Sopenharmony_ci				data->clk_flags | CLK_SET_RATE_PARENT,
758c2ecf20Sopenharmony_ci				reg + data->offset,
768c2ecf20Sopenharmony_ci				data->shift, data->width, data->mux_flags,
778c2ecf20Sopenharmony_ci				data->lock);
788c2ecf20Sopenharmony_ci	if (IS_ERR(clk))
798c2ecf20Sopenharmony_ci		goto err;
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	pr_debug("%s: parent %s rate %u\n",
828c2ecf20Sopenharmony_ci			__clk_get_name(clk),
838c2ecf20Sopenharmony_ci			__clk_get_name(clk_get_parent(clk)),
848c2ecf20Sopenharmony_ci			(unsigned int)clk_get_rate(clk));
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	kfree(parents);
878c2ecf20Sopenharmony_ci	of_clk_add_provider(np, of_clk_src_simple_get, clk);
888c2ecf20Sopenharmony_ci	return;
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_cierr:
918c2ecf20Sopenharmony_ci	kfree(parents);
928c2ecf20Sopenharmony_cierr_parents:
938c2ecf20Sopenharmony_ci	iounmap(reg);
948c2ecf20Sopenharmony_ci}
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_cistatic void __init st_of_clkgen_a9_mux_setup(struct device_node *np)
978c2ecf20Sopenharmony_ci{
988c2ecf20Sopenharmony_ci	st_of_clkgen_mux_setup(np, &stih407_a9_mux_data);
998c2ecf20Sopenharmony_ci}
1008c2ecf20Sopenharmony_ciCLK_OF_DECLARE(clkgen_a9mux, "st,stih407-clkgen-a9-mux",
1018c2ecf20Sopenharmony_ci		st_of_clkgen_a9_mux_setup);
102