162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * clkgen-mux.c: ST GEN-MUX Clock driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2014 STMicroelectronics (R&D) Limited
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Authors: Stephen Gallimore <stephen.gallimore@st.com>
862306a36Sopenharmony_ci *	    Pankaj Dev <pankaj.dev@st.com>
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/slab.h>
1262306a36Sopenharmony_ci#include <linux/io.h>
1362306a36Sopenharmony_ci#include <linux/of_address.h>
1462306a36Sopenharmony_ci#include <linux/clk.h>
1562306a36Sopenharmony_ci#include <linux/clk-provider.h>
1662306a36Sopenharmony_ci#include "clkgen.h"
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_cistatic const char ** __init clkgen_mux_get_parents(struct device_node *np,
1962306a36Sopenharmony_ci						       int *num_parents)
2062306a36Sopenharmony_ci{
2162306a36Sopenharmony_ci	const char **parents;
2262306a36Sopenharmony_ci	unsigned int nparents;
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci	nparents = of_clk_get_parent_count(np);
2562306a36Sopenharmony_ci	if (WARN_ON(!nparents))
2662306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci	parents = kcalloc(nparents, sizeof(const char *), GFP_KERNEL);
2962306a36Sopenharmony_ci	if (!parents)
3062306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci	*num_parents = of_clk_parent_fill(np, parents, nparents);
3362306a36Sopenharmony_ci	return parents;
3462306a36Sopenharmony_ci}
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_cistruct clkgen_mux_data {
3762306a36Sopenharmony_ci	u32 offset;
3862306a36Sopenharmony_ci	u8 shift;
3962306a36Sopenharmony_ci	u8 width;
4062306a36Sopenharmony_ci	spinlock_t *lock;
4162306a36Sopenharmony_ci	unsigned long clk_flags;
4262306a36Sopenharmony_ci	u8 mux_flags;
4362306a36Sopenharmony_ci};
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_cistatic struct clkgen_mux_data stih407_a9_mux_data = {
4662306a36Sopenharmony_ci	.offset = 0x1a4,
4762306a36Sopenharmony_ci	.shift = 0,
4862306a36Sopenharmony_ci	.width = 2,
4962306a36Sopenharmony_ci	.lock = &clkgen_a9_lock,
5062306a36Sopenharmony_ci};
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_cistatic void __init st_of_clkgen_mux_setup(struct device_node *np,
5362306a36Sopenharmony_ci		struct clkgen_mux_data *data)
5462306a36Sopenharmony_ci{
5562306a36Sopenharmony_ci	struct clk *clk;
5662306a36Sopenharmony_ci	void __iomem *reg;
5762306a36Sopenharmony_ci	const char **parents;
5862306a36Sopenharmony_ci	int num_parents = 0;
5962306a36Sopenharmony_ci	struct device_node *parent_np;
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	/*
6262306a36Sopenharmony_ci	 * First check for reg property within the node to keep backward
6362306a36Sopenharmony_ci	 * compatibility, then if reg doesn't exist look at the parent node
6462306a36Sopenharmony_ci	 */
6562306a36Sopenharmony_ci	reg = of_iomap(np, 0);
6662306a36Sopenharmony_ci	if (!reg) {
6762306a36Sopenharmony_ci		parent_np = of_get_parent(np);
6862306a36Sopenharmony_ci		reg = of_iomap(parent_np, 0);
6962306a36Sopenharmony_ci		of_node_put(parent_np);
7062306a36Sopenharmony_ci		if (!reg) {
7162306a36Sopenharmony_ci			pr_err("%s: Failed to get base address\n", __func__);
7262306a36Sopenharmony_ci			return;
7362306a36Sopenharmony_ci		}
7462306a36Sopenharmony_ci	}
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	parents = clkgen_mux_get_parents(np, &num_parents);
7762306a36Sopenharmony_ci	if (IS_ERR(parents)) {
7862306a36Sopenharmony_ci		pr_err("%s: Failed to get parents (%ld)\n",
7962306a36Sopenharmony_ci				__func__, PTR_ERR(parents));
8062306a36Sopenharmony_ci		goto err_parents;
8162306a36Sopenharmony_ci	}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	clk = clk_register_mux(NULL, np->name, parents, num_parents,
8462306a36Sopenharmony_ci				data->clk_flags | CLK_SET_RATE_PARENT,
8562306a36Sopenharmony_ci				reg + data->offset,
8662306a36Sopenharmony_ci				data->shift, data->width, data->mux_flags,
8762306a36Sopenharmony_ci				data->lock);
8862306a36Sopenharmony_ci	if (IS_ERR(clk))
8962306a36Sopenharmony_ci		goto err;
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	pr_debug("%s: parent %s rate %u\n",
9262306a36Sopenharmony_ci			__clk_get_name(clk),
9362306a36Sopenharmony_ci			__clk_get_name(clk_get_parent(clk)),
9462306a36Sopenharmony_ci			(unsigned int)clk_get_rate(clk));
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	kfree(parents);
9762306a36Sopenharmony_ci	of_clk_add_provider(np, of_clk_src_simple_get, clk);
9862306a36Sopenharmony_ci	return;
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_cierr:
10162306a36Sopenharmony_ci	kfree(parents);
10262306a36Sopenharmony_cierr_parents:
10362306a36Sopenharmony_ci	iounmap(reg);
10462306a36Sopenharmony_ci}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_cistatic void __init st_of_clkgen_a9_mux_setup(struct device_node *np)
10762306a36Sopenharmony_ci{
10862306a36Sopenharmony_ci	st_of_clkgen_mux_setup(np, &stih407_a9_mux_data);
10962306a36Sopenharmony_ci}
11062306a36Sopenharmony_ciCLK_OF_DECLARE(clkgen_a9mux, "st,stih407-clkgen-a9-mux",
11162306a36Sopenharmony_ci		st_of_clkgen_a9_mux_setup);
112