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