194885fafSGabriel FERNANDEZ /* 294885fafSGabriel FERNANDEZ * clkgen-mux.c: ST GEN-MUX Clock driver 394885fafSGabriel FERNANDEZ * 494885fafSGabriel FERNANDEZ * Copyright (C) 2014 STMicroelectronics (R&D) Limited 594885fafSGabriel FERNANDEZ * 694885fafSGabriel FERNANDEZ * Authors: Stephen Gallimore <stephen.gallimore@st.com> 794885fafSGabriel FERNANDEZ * Pankaj Dev <pankaj.dev@st.com> 894885fafSGabriel FERNANDEZ * 994885fafSGabriel FERNANDEZ * This program is free software; you can redistribute it and/or modify 1094885fafSGabriel FERNANDEZ * it under the terms of the GNU General Public License as published by 1194885fafSGabriel FERNANDEZ * the Free Software Foundation; either version 2 of the License, or 1294885fafSGabriel FERNANDEZ * (at your option) any later version. 1394885fafSGabriel FERNANDEZ * 1494885fafSGabriel FERNANDEZ */ 1594885fafSGabriel FERNANDEZ 1694885fafSGabriel FERNANDEZ #include <linux/slab.h> 1794885fafSGabriel FERNANDEZ #include <linux/of_address.h> 18d5f728acSStephen Boyd #include <linux/clk.h> 1994885fafSGabriel FERNANDEZ #include <linux/clk-provider.h> 2046a57afdSGabriel Fernandez #include "clkgen.h" 2194885fafSGabriel FERNANDEZ 2294885fafSGabriel FERNANDEZ static const char ** __init clkgen_mux_get_parents(struct device_node *np, 2394885fafSGabriel FERNANDEZ int *num_parents) 2494885fafSGabriel FERNANDEZ { 2594885fafSGabriel FERNANDEZ const char **parents; 26caeb057cSStephen Boyd unsigned int nparents; 2794885fafSGabriel FERNANDEZ 280a65239cSGeert Uytterhoeven nparents = of_clk_get_parent_count(np); 29caeb057cSStephen Boyd if (WARN_ON(!nparents)) 3094885fafSGabriel FERNANDEZ return ERR_PTR(-EINVAL); 3194885fafSGabriel FERNANDEZ 3286665d28SStephen Boyd parents = kcalloc(nparents, sizeof(const char *), GFP_KERNEL); 3394885fafSGabriel FERNANDEZ if (!parents) 3494885fafSGabriel FERNANDEZ return ERR_PTR(-ENOMEM); 3594885fafSGabriel FERNANDEZ 360b4e7f08SDinh Nguyen *num_parents = of_clk_parent_fill(np, parents, nparents); 3794885fafSGabriel FERNANDEZ return parents; 3894885fafSGabriel FERNANDEZ } 3994885fafSGabriel FERNANDEZ 4044993d38SGabriel FERNANDEZ struct clkgen_mux_data { 4144993d38SGabriel FERNANDEZ u32 offset; 4244993d38SGabriel FERNANDEZ u8 shift; 4344993d38SGabriel FERNANDEZ u8 width; 4444993d38SGabriel FERNANDEZ spinlock_t *lock; 4544993d38SGabriel FERNANDEZ unsigned long clk_flags; 4644993d38SGabriel FERNANDEZ u8 mux_flags; 4744993d38SGabriel FERNANDEZ }; 4844993d38SGabriel FERNANDEZ 4913e6f2daSGabriel FERNANDEZ static struct clkgen_mux_data stih407_a9_mux_data = { 5013e6f2daSGabriel FERNANDEZ .offset = 0x1a4, 513be6d8ceSGabriel Fernandez .shift = 0, 5213e6f2daSGabriel FERNANDEZ .width = 2, 5346a57afdSGabriel Fernandez .lock = &clkgen_a9_lock, 5413e6f2daSGabriel FERNANDEZ }; 55ab35dc13SGabriel FERNANDEZ 56f375573cSFabian Frederick static const struct of_device_id mux_of_match[] = { 5744993d38SGabriel FERNANDEZ { 5813e6f2daSGabriel FERNANDEZ .compatible = "st,stih407-clkgen-a9-mux", 5913e6f2daSGabriel FERNANDEZ .data = &stih407_a9_mux_data, 6013e6f2daSGabriel FERNANDEZ }, 6144993d38SGabriel FERNANDEZ {} 6244993d38SGabriel FERNANDEZ }; 638e6dd77cSStephen Boyd static void __init st_of_clkgen_mux_setup(struct device_node *np) 6444993d38SGabriel FERNANDEZ { 6544993d38SGabriel FERNANDEZ const struct of_device_id *match; 6644993d38SGabriel FERNANDEZ struct clk *clk; 6744993d38SGabriel FERNANDEZ void __iomem *reg; 6844993d38SGabriel FERNANDEZ const char **parents; 69*7df404c9SGabriel Fernandez int num_parents = 0; 7086665d28SStephen Boyd const struct clkgen_mux_data *data; 7144993d38SGabriel FERNANDEZ 7244993d38SGabriel FERNANDEZ match = of_match_node(mux_of_match, np); 7344993d38SGabriel FERNANDEZ if (!match) { 7444993d38SGabriel FERNANDEZ pr_err("%s: No matching data\n", __func__); 7544993d38SGabriel FERNANDEZ return; 7644993d38SGabriel FERNANDEZ } 7744993d38SGabriel FERNANDEZ 7886665d28SStephen Boyd data = match->data; 7944993d38SGabriel FERNANDEZ 8044993d38SGabriel FERNANDEZ reg = of_iomap(np, 0); 8144993d38SGabriel FERNANDEZ if (!reg) { 8244993d38SGabriel FERNANDEZ pr_err("%s: Failed to get base address\n", __func__); 8344993d38SGabriel FERNANDEZ return; 8444993d38SGabriel FERNANDEZ } 8544993d38SGabriel FERNANDEZ 8644993d38SGabriel FERNANDEZ parents = clkgen_mux_get_parents(np, &num_parents); 8744993d38SGabriel FERNANDEZ if (IS_ERR(parents)) { 8844993d38SGabriel FERNANDEZ pr_err("%s: Failed to get parents (%ld)\n", 8944993d38SGabriel FERNANDEZ __func__, PTR_ERR(parents)); 9086665d28SStephen Boyd goto err_parents; 9144993d38SGabriel FERNANDEZ } 9244993d38SGabriel FERNANDEZ 9344993d38SGabriel FERNANDEZ clk = clk_register_mux(NULL, np->name, parents, num_parents, 9444993d38SGabriel FERNANDEZ data->clk_flags | CLK_SET_RATE_PARENT, 9544993d38SGabriel FERNANDEZ reg + data->offset, 9644993d38SGabriel FERNANDEZ data->shift, data->width, data->mux_flags, 9744993d38SGabriel FERNANDEZ data->lock); 9844993d38SGabriel FERNANDEZ if (IS_ERR(clk)) 9944993d38SGabriel FERNANDEZ goto err; 10044993d38SGabriel FERNANDEZ 10144993d38SGabriel FERNANDEZ pr_debug("%s: parent %s rate %u\n", 10244993d38SGabriel FERNANDEZ __clk_get_name(clk), 10344993d38SGabriel FERNANDEZ __clk_get_name(clk_get_parent(clk)), 10444993d38SGabriel FERNANDEZ (unsigned int)clk_get_rate(clk)); 10544993d38SGabriel FERNANDEZ 10686665d28SStephen Boyd kfree(parents); 10744993d38SGabriel FERNANDEZ of_clk_add_provider(np, of_clk_src_simple_get, clk); 10886665d28SStephen Boyd return; 10944993d38SGabriel FERNANDEZ 11044993d38SGabriel FERNANDEZ err: 11144993d38SGabriel FERNANDEZ kfree(parents); 11286665d28SStephen Boyd err_parents: 11386665d28SStephen Boyd iounmap(reg); 11444993d38SGabriel FERNANDEZ } 11544993d38SGabriel FERNANDEZ CLK_OF_DECLARE(clkgen_mux, "st,clkgen-mux", st_of_clkgen_mux_setup); 116