12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
294885fafSGabriel FERNANDEZ /*
394885fafSGabriel FERNANDEZ * clkgen-mux.c: ST GEN-MUX Clock driver
494885fafSGabriel FERNANDEZ *
594885fafSGabriel FERNANDEZ * Copyright (C) 2014 STMicroelectronics (R&D) Limited
694885fafSGabriel FERNANDEZ *
794885fafSGabriel FERNANDEZ * Authors: Stephen Gallimore <stephen.gallimore@st.com>
894885fafSGabriel FERNANDEZ * Pankaj Dev <pankaj.dev@st.com>
994885fafSGabriel FERNANDEZ */
1094885fafSGabriel FERNANDEZ
1194885fafSGabriel FERNANDEZ #include <linux/slab.h>
1262e59c4eSStephen Boyd #include <linux/io.h>
1394885fafSGabriel FERNANDEZ #include <linux/of_address.h>
14d5f728acSStephen Boyd #include <linux/clk.h>
1594885fafSGabriel FERNANDEZ #include <linux/clk-provider.h>
1646a57afdSGabriel Fernandez #include "clkgen.h"
1794885fafSGabriel FERNANDEZ
clkgen_mux_get_parents(struct device_node * np,int * num_parents)1894885fafSGabriel FERNANDEZ static const char ** __init clkgen_mux_get_parents(struct device_node *np,
1994885fafSGabriel FERNANDEZ int *num_parents)
2094885fafSGabriel FERNANDEZ {
2194885fafSGabriel FERNANDEZ const char **parents;
22caeb057cSStephen Boyd unsigned int nparents;
2394885fafSGabriel FERNANDEZ
240a65239cSGeert Uytterhoeven nparents = of_clk_get_parent_count(np);
25caeb057cSStephen Boyd if (WARN_ON(!nparents))
2694885fafSGabriel FERNANDEZ return ERR_PTR(-EINVAL);
2794885fafSGabriel FERNANDEZ
2886665d28SStephen Boyd parents = kcalloc(nparents, sizeof(const char *), GFP_KERNEL);
2994885fafSGabriel FERNANDEZ if (!parents)
3094885fafSGabriel FERNANDEZ return ERR_PTR(-ENOMEM);
3194885fafSGabriel FERNANDEZ
320b4e7f08SDinh Nguyen *num_parents = of_clk_parent_fill(np, parents, nparents);
3394885fafSGabriel FERNANDEZ return parents;
3494885fafSGabriel FERNANDEZ }
3594885fafSGabriel FERNANDEZ
3644993d38SGabriel FERNANDEZ struct clkgen_mux_data {
3744993d38SGabriel FERNANDEZ u32 offset;
3844993d38SGabriel FERNANDEZ u8 shift;
3944993d38SGabriel FERNANDEZ u8 width;
4044993d38SGabriel FERNANDEZ spinlock_t *lock;
4144993d38SGabriel FERNANDEZ unsigned long clk_flags;
4244993d38SGabriel FERNANDEZ u8 mux_flags;
4344993d38SGabriel FERNANDEZ };
4444993d38SGabriel FERNANDEZ
4513e6f2daSGabriel FERNANDEZ static struct clkgen_mux_data stih407_a9_mux_data = {
4613e6f2daSGabriel FERNANDEZ .offset = 0x1a4,
473be6d8ceSGabriel Fernandez .shift = 0,
4813e6f2daSGabriel FERNANDEZ .width = 2,
4946a57afdSGabriel Fernandez .lock = &clkgen_a9_lock,
5013e6f2daSGabriel FERNANDEZ };
51ab35dc13SGabriel FERNANDEZ
st_of_clkgen_mux_setup(struct device_node * np,struct clkgen_mux_data * data)52880d54ffSGabriel Fernandez static void __init st_of_clkgen_mux_setup(struct device_node *np,
53880d54ffSGabriel Fernandez struct clkgen_mux_data *data)
5444993d38SGabriel FERNANDEZ {
5544993d38SGabriel FERNANDEZ struct clk *clk;
5644993d38SGabriel FERNANDEZ void __iomem *reg;
5744993d38SGabriel FERNANDEZ const char **parents;
587df404c9SGabriel Fernandez int num_parents = 0;
59*42997330SLiang He struct device_node *parent_np;
6044993d38SGabriel FERNANDEZ
61810251b0SAlain Volmat /*
62810251b0SAlain Volmat * First check for reg property within the node to keep backward
63810251b0SAlain Volmat * compatibility, then if reg doesn't exist look at the parent node
64810251b0SAlain Volmat */
6544993d38SGabriel FERNANDEZ reg = of_iomap(np, 0);
6644993d38SGabriel FERNANDEZ if (!reg) {
67*42997330SLiang He parent_np = of_get_parent(np);
68*42997330SLiang He reg = of_iomap(parent_np, 0);
69*42997330SLiang He of_node_put(parent_np);
70810251b0SAlain Volmat if (!reg) {
7144993d38SGabriel FERNANDEZ pr_err("%s: Failed to get base address\n", __func__);
7244993d38SGabriel FERNANDEZ return;
7344993d38SGabriel FERNANDEZ }
74810251b0SAlain Volmat }
7544993d38SGabriel FERNANDEZ
7644993d38SGabriel FERNANDEZ parents = clkgen_mux_get_parents(np, &num_parents);
7744993d38SGabriel FERNANDEZ if (IS_ERR(parents)) {
7844993d38SGabriel FERNANDEZ pr_err("%s: Failed to get parents (%ld)\n",
7944993d38SGabriel FERNANDEZ __func__, PTR_ERR(parents));
8086665d28SStephen Boyd goto err_parents;
8144993d38SGabriel FERNANDEZ }
8244993d38SGabriel FERNANDEZ
8344993d38SGabriel FERNANDEZ clk = clk_register_mux(NULL, np->name, parents, num_parents,
8444993d38SGabriel FERNANDEZ data->clk_flags | CLK_SET_RATE_PARENT,
8544993d38SGabriel FERNANDEZ reg + data->offset,
8644993d38SGabriel FERNANDEZ data->shift, data->width, data->mux_flags,
8744993d38SGabriel FERNANDEZ data->lock);
8844993d38SGabriel FERNANDEZ if (IS_ERR(clk))
8944993d38SGabriel FERNANDEZ goto err;
9044993d38SGabriel FERNANDEZ
9144993d38SGabriel FERNANDEZ pr_debug("%s: parent %s rate %u\n",
9244993d38SGabriel FERNANDEZ __clk_get_name(clk),
9344993d38SGabriel FERNANDEZ __clk_get_name(clk_get_parent(clk)),
9444993d38SGabriel FERNANDEZ (unsigned int)clk_get_rate(clk));
9544993d38SGabriel FERNANDEZ
9686665d28SStephen Boyd kfree(parents);
9744993d38SGabriel FERNANDEZ of_clk_add_provider(np, of_clk_src_simple_get, clk);
9886665d28SStephen Boyd return;
9944993d38SGabriel FERNANDEZ
10044993d38SGabriel FERNANDEZ err:
10144993d38SGabriel FERNANDEZ kfree(parents);
10286665d28SStephen Boyd err_parents:
10386665d28SStephen Boyd iounmap(reg);
10444993d38SGabriel FERNANDEZ }
105880d54ffSGabriel Fernandez
st_of_clkgen_a9_mux_setup(struct device_node * np)106880d54ffSGabriel Fernandez static void __init st_of_clkgen_a9_mux_setup(struct device_node *np)
107880d54ffSGabriel Fernandez {
108880d54ffSGabriel Fernandez st_of_clkgen_mux_setup(np, &stih407_a9_mux_data);
109880d54ffSGabriel Fernandez }
110880d54ffSGabriel Fernandez CLK_OF_DECLARE(clkgen_a9mux, "st,stih407-clkgen-a9-mux",
111880d54ffSGabriel Fernandez st_of_clkgen_a9_mux_setup);
112