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> 17*62e59c4eSStephen Boyd #include <linux/io.h> 1894885fafSGabriel FERNANDEZ #include <linux/of_address.h> 19d5f728acSStephen Boyd #include <linux/clk.h> 2094885fafSGabriel FERNANDEZ #include <linux/clk-provider.h> 2146a57afdSGabriel Fernandez #include "clkgen.h" 2294885fafSGabriel FERNANDEZ 2394885fafSGabriel FERNANDEZ static const char ** __init clkgen_mux_get_parents(struct device_node *np, 2494885fafSGabriel FERNANDEZ int *num_parents) 2594885fafSGabriel FERNANDEZ { 2694885fafSGabriel FERNANDEZ const char **parents; 27caeb057cSStephen Boyd unsigned int nparents; 2894885fafSGabriel FERNANDEZ 290a65239cSGeert Uytterhoeven nparents = of_clk_get_parent_count(np); 30caeb057cSStephen Boyd if (WARN_ON(!nparents)) 3194885fafSGabriel FERNANDEZ return ERR_PTR(-EINVAL); 3294885fafSGabriel FERNANDEZ 3386665d28SStephen Boyd parents = kcalloc(nparents, sizeof(const char *), GFP_KERNEL); 3494885fafSGabriel FERNANDEZ if (!parents) 3594885fafSGabriel FERNANDEZ return ERR_PTR(-ENOMEM); 3694885fafSGabriel FERNANDEZ 370b4e7f08SDinh Nguyen *num_parents = of_clk_parent_fill(np, parents, nparents); 3894885fafSGabriel FERNANDEZ return parents; 3994885fafSGabriel FERNANDEZ } 4094885fafSGabriel FERNANDEZ 4144993d38SGabriel FERNANDEZ struct clkgen_mux_data { 4244993d38SGabriel FERNANDEZ u32 offset; 4344993d38SGabriel FERNANDEZ u8 shift; 4444993d38SGabriel FERNANDEZ u8 width; 4544993d38SGabriel FERNANDEZ spinlock_t *lock; 4644993d38SGabriel FERNANDEZ unsigned long clk_flags; 4744993d38SGabriel FERNANDEZ u8 mux_flags; 4844993d38SGabriel FERNANDEZ }; 4944993d38SGabriel FERNANDEZ 5013e6f2daSGabriel FERNANDEZ static struct clkgen_mux_data stih407_a9_mux_data = { 5113e6f2daSGabriel FERNANDEZ .offset = 0x1a4, 523be6d8ceSGabriel Fernandez .shift = 0, 5313e6f2daSGabriel FERNANDEZ .width = 2, 5446a57afdSGabriel Fernandez .lock = &clkgen_a9_lock, 5513e6f2daSGabriel FERNANDEZ }; 56ab35dc13SGabriel FERNANDEZ 57880d54ffSGabriel Fernandez static void __init st_of_clkgen_mux_setup(struct device_node *np, 58880d54ffSGabriel Fernandez struct clkgen_mux_data *data) 5944993d38SGabriel FERNANDEZ { 6044993d38SGabriel FERNANDEZ struct clk *clk; 6144993d38SGabriel FERNANDEZ void __iomem *reg; 6244993d38SGabriel FERNANDEZ const char **parents; 637df404c9SGabriel Fernandez int num_parents = 0; 6444993d38SGabriel FERNANDEZ 6544993d38SGabriel FERNANDEZ reg = of_iomap(np, 0); 6644993d38SGabriel FERNANDEZ if (!reg) { 6744993d38SGabriel FERNANDEZ pr_err("%s: Failed to get base address\n", __func__); 6844993d38SGabriel FERNANDEZ return; 6944993d38SGabriel FERNANDEZ } 7044993d38SGabriel FERNANDEZ 7144993d38SGabriel FERNANDEZ parents = clkgen_mux_get_parents(np, &num_parents); 7244993d38SGabriel FERNANDEZ if (IS_ERR(parents)) { 7344993d38SGabriel FERNANDEZ pr_err("%s: Failed to get parents (%ld)\n", 7444993d38SGabriel FERNANDEZ __func__, PTR_ERR(parents)); 7586665d28SStephen Boyd goto err_parents; 7644993d38SGabriel FERNANDEZ } 7744993d38SGabriel FERNANDEZ 7844993d38SGabriel FERNANDEZ clk = clk_register_mux(NULL, np->name, parents, num_parents, 7944993d38SGabriel FERNANDEZ data->clk_flags | CLK_SET_RATE_PARENT, 8044993d38SGabriel FERNANDEZ reg + data->offset, 8144993d38SGabriel FERNANDEZ data->shift, data->width, data->mux_flags, 8244993d38SGabriel FERNANDEZ data->lock); 8344993d38SGabriel FERNANDEZ if (IS_ERR(clk)) 8444993d38SGabriel FERNANDEZ goto err; 8544993d38SGabriel FERNANDEZ 8644993d38SGabriel FERNANDEZ pr_debug("%s: parent %s rate %u\n", 8744993d38SGabriel FERNANDEZ __clk_get_name(clk), 8844993d38SGabriel FERNANDEZ __clk_get_name(clk_get_parent(clk)), 8944993d38SGabriel FERNANDEZ (unsigned int)clk_get_rate(clk)); 9044993d38SGabriel FERNANDEZ 9186665d28SStephen Boyd kfree(parents); 9244993d38SGabriel FERNANDEZ of_clk_add_provider(np, of_clk_src_simple_get, clk); 9386665d28SStephen Boyd return; 9444993d38SGabriel FERNANDEZ 9544993d38SGabriel FERNANDEZ err: 9644993d38SGabriel FERNANDEZ kfree(parents); 9786665d28SStephen Boyd err_parents: 9886665d28SStephen Boyd iounmap(reg); 9944993d38SGabriel FERNANDEZ } 100880d54ffSGabriel Fernandez 101880d54ffSGabriel Fernandez static void __init st_of_clkgen_a9_mux_setup(struct device_node *np) 102880d54ffSGabriel Fernandez { 103880d54ffSGabriel Fernandez st_of_clkgen_mux_setup(np, &stih407_a9_mux_data); 104880d54ffSGabriel Fernandez } 105880d54ffSGabriel Fernandez CLK_OF_DECLARE(clkgen_a9mux, "st,stih407-clkgen-a9-mux", 106880d54ffSGabriel Fernandez st_of_clkgen_a9_mux_setup); 107