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 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 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 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