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> 1894885fafSGabriel FERNANDEZ #include <linux/clk-provider.h> 1994885fafSGabriel FERNANDEZ 2094885fafSGabriel FERNANDEZ static DEFINE_SPINLOCK(clkgena_divmux_lock); 2144993d38SGabriel FERNANDEZ static DEFINE_SPINLOCK(clkgenf_lock); 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; 27*0b4e7f08SDinh Nguyen int nparents; 2894885fafSGabriel FERNANDEZ 290a65239cSGeert Uytterhoeven nparents = of_clk_get_parent_count(np); 3094885fafSGabriel FERNANDEZ if (WARN_ON(nparents <= 0)) 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 37*0b4e7f08SDinh Nguyen *num_parents = of_clk_parent_fill(np, parents, nparents); 3894885fafSGabriel FERNANDEZ return parents; 3994885fafSGabriel FERNANDEZ } 4094885fafSGabriel FERNANDEZ 4194885fafSGabriel FERNANDEZ /** 4294885fafSGabriel FERNANDEZ * DOC: Clock mux with a programmable divider on each of its three inputs. 4394885fafSGabriel FERNANDEZ * The mux has an input setting which effectively gates its output. 4494885fafSGabriel FERNANDEZ * 4594885fafSGabriel FERNANDEZ * Traits of this clock: 4694885fafSGabriel FERNANDEZ * prepare - clk_(un)prepare only ensures parent is (un)prepared 4794885fafSGabriel FERNANDEZ * enable - clk_enable and clk_disable are functional & control gating 4894885fafSGabriel FERNANDEZ * rate - set rate is supported 4994885fafSGabriel FERNANDEZ * parent - set/get parent 5094885fafSGabriel FERNANDEZ */ 5194885fafSGabriel FERNANDEZ 5294885fafSGabriel FERNANDEZ #define NUM_INPUTS 3 5394885fafSGabriel FERNANDEZ 5494885fafSGabriel FERNANDEZ struct clkgena_divmux { 5594885fafSGabriel FERNANDEZ struct clk_hw hw; 5694885fafSGabriel FERNANDEZ /* Subclassed mux and divider structures */ 5794885fafSGabriel FERNANDEZ struct clk_mux mux; 5894885fafSGabriel FERNANDEZ struct clk_divider div[NUM_INPUTS]; 5994885fafSGabriel FERNANDEZ /* Enable/running feedback register bits for each input */ 6094885fafSGabriel FERNANDEZ void __iomem *feedback_reg[NUM_INPUTS]; 6194885fafSGabriel FERNANDEZ int feedback_bit_idx; 6294885fafSGabriel FERNANDEZ 6394885fafSGabriel FERNANDEZ u8 muxsel; 6494885fafSGabriel FERNANDEZ }; 6594885fafSGabriel FERNANDEZ 6694885fafSGabriel FERNANDEZ #define to_clkgena_divmux(_hw) container_of(_hw, struct clkgena_divmux, hw) 6794885fafSGabriel FERNANDEZ 6894885fafSGabriel FERNANDEZ struct clkgena_divmux_data { 6994885fafSGabriel FERNANDEZ int num_outputs; 7094885fafSGabriel FERNANDEZ int mux_offset; 7194885fafSGabriel FERNANDEZ int mux_offset2; 7294885fafSGabriel FERNANDEZ int mux_start_bit; 7394885fafSGabriel FERNANDEZ int div_offsets[NUM_INPUTS]; 7494885fafSGabriel FERNANDEZ int fb_offsets[NUM_INPUTS]; 7594885fafSGabriel FERNANDEZ int fb_start_bit_idx; 7694885fafSGabriel FERNANDEZ }; 7794885fafSGabriel FERNANDEZ 7894885fafSGabriel FERNANDEZ #define CKGAX_CLKOPSRC_SWITCH_OFF 0x3 7994885fafSGabriel FERNANDEZ 8094885fafSGabriel FERNANDEZ static int clkgena_divmux_is_running(struct clkgena_divmux *mux) 8194885fafSGabriel FERNANDEZ { 8294885fafSGabriel FERNANDEZ u32 regval = readl(mux->feedback_reg[mux->muxsel]); 8394885fafSGabriel FERNANDEZ u32 running = regval & BIT(mux->feedback_bit_idx); 8494885fafSGabriel FERNANDEZ return !!running; 8594885fafSGabriel FERNANDEZ } 8694885fafSGabriel FERNANDEZ 8794885fafSGabriel FERNANDEZ static int clkgena_divmux_enable(struct clk_hw *hw) 8894885fafSGabriel FERNANDEZ { 8994885fafSGabriel FERNANDEZ struct clkgena_divmux *genamux = to_clkgena_divmux(hw); 9094885fafSGabriel FERNANDEZ struct clk_hw *mux_hw = &genamux->mux.hw; 9194885fafSGabriel FERNANDEZ unsigned long timeout; 9294885fafSGabriel FERNANDEZ int ret = 0; 9394885fafSGabriel FERNANDEZ 944e907ef6SJavier Martinez Canillas __clk_hw_set_clk(mux_hw, hw); 9594885fafSGabriel FERNANDEZ 9694885fafSGabriel FERNANDEZ ret = clk_mux_ops.set_parent(mux_hw, genamux->muxsel); 9794885fafSGabriel FERNANDEZ if (ret) 9894885fafSGabriel FERNANDEZ return ret; 9994885fafSGabriel FERNANDEZ 10094885fafSGabriel FERNANDEZ timeout = jiffies + msecs_to_jiffies(10); 10194885fafSGabriel FERNANDEZ 10294885fafSGabriel FERNANDEZ while (!clkgena_divmux_is_running(genamux)) { 10394885fafSGabriel FERNANDEZ if (time_after(jiffies, timeout)) 10494885fafSGabriel FERNANDEZ return -ETIMEDOUT; 10594885fafSGabriel FERNANDEZ cpu_relax(); 10694885fafSGabriel FERNANDEZ } 10794885fafSGabriel FERNANDEZ 10894885fafSGabriel FERNANDEZ return 0; 10994885fafSGabriel FERNANDEZ } 11094885fafSGabriel FERNANDEZ 11194885fafSGabriel FERNANDEZ static void clkgena_divmux_disable(struct clk_hw *hw) 11294885fafSGabriel FERNANDEZ { 11394885fafSGabriel FERNANDEZ struct clkgena_divmux *genamux = to_clkgena_divmux(hw); 11494885fafSGabriel FERNANDEZ struct clk_hw *mux_hw = &genamux->mux.hw; 11594885fafSGabriel FERNANDEZ 1164e907ef6SJavier Martinez Canillas __clk_hw_set_clk(mux_hw, hw); 11794885fafSGabriel FERNANDEZ 11894885fafSGabriel FERNANDEZ clk_mux_ops.set_parent(mux_hw, CKGAX_CLKOPSRC_SWITCH_OFF); 11994885fafSGabriel FERNANDEZ } 12094885fafSGabriel FERNANDEZ 12194885fafSGabriel FERNANDEZ static int clkgena_divmux_is_enabled(struct clk_hw *hw) 12294885fafSGabriel FERNANDEZ { 12394885fafSGabriel FERNANDEZ struct clkgena_divmux *genamux = to_clkgena_divmux(hw); 12494885fafSGabriel FERNANDEZ struct clk_hw *mux_hw = &genamux->mux.hw; 12594885fafSGabriel FERNANDEZ 1264e907ef6SJavier Martinez Canillas __clk_hw_set_clk(mux_hw, hw); 12794885fafSGabriel FERNANDEZ 12894885fafSGabriel FERNANDEZ return (s8)clk_mux_ops.get_parent(mux_hw) > 0; 12994885fafSGabriel FERNANDEZ } 13094885fafSGabriel FERNANDEZ 1318e6dd77cSStephen Boyd static u8 clkgena_divmux_get_parent(struct clk_hw *hw) 13294885fafSGabriel FERNANDEZ { 13394885fafSGabriel FERNANDEZ struct clkgena_divmux *genamux = to_clkgena_divmux(hw); 13494885fafSGabriel FERNANDEZ struct clk_hw *mux_hw = &genamux->mux.hw; 13594885fafSGabriel FERNANDEZ 1364e907ef6SJavier Martinez Canillas __clk_hw_set_clk(mux_hw, hw); 13794885fafSGabriel FERNANDEZ 13894885fafSGabriel FERNANDEZ genamux->muxsel = clk_mux_ops.get_parent(mux_hw); 13994885fafSGabriel FERNANDEZ if ((s8)genamux->muxsel < 0) { 14094885fafSGabriel FERNANDEZ pr_debug("%s: %s: Invalid parent, setting to default.\n", 14194885fafSGabriel FERNANDEZ __func__, __clk_get_name(hw->clk)); 14294885fafSGabriel FERNANDEZ genamux->muxsel = 0; 14394885fafSGabriel FERNANDEZ } 14494885fafSGabriel FERNANDEZ 14594885fafSGabriel FERNANDEZ return genamux->muxsel; 14694885fafSGabriel FERNANDEZ } 14794885fafSGabriel FERNANDEZ 14894885fafSGabriel FERNANDEZ static int clkgena_divmux_set_parent(struct clk_hw *hw, u8 index) 14994885fafSGabriel FERNANDEZ { 15094885fafSGabriel FERNANDEZ struct clkgena_divmux *genamux = to_clkgena_divmux(hw); 15194885fafSGabriel FERNANDEZ 15294885fafSGabriel FERNANDEZ if (index >= CKGAX_CLKOPSRC_SWITCH_OFF) 15394885fafSGabriel FERNANDEZ return -EINVAL; 15494885fafSGabriel FERNANDEZ 15594885fafSGabriel FERNANDEZ genamux->muxsel = index; 15694885fafSGabriel FERNANDEZ 15794885fafSGabriel FERNANDEZ /* 15894885fafSGabriel FERNANDEZ * If the mux is already enabled, call enable directly to set the 15994885fafSGabriel FERNANDEZ * new mux position and wait for it to start running again. Otherwise 16094885fafSGabriel FERNANDEZ * do nothing. 16194885fafSGabriel FERNANDEZ */ 16294885fafSGabriel FERNANDEZ if (clkgena_divmux_is_enabled(hw)) 16394885fafSGabriel FERNANDEZ clkgena_divmux_enable(hw); 16494885fafSGabriel FERNANDEZ 16594885fafSGabriel FERNANDEZ return 0; 16694885fafSGabriel FERNANDEZ } 16794885fafSGabriel FERNANDEZ 1688e6dd77cSStephen Boyd static unsigned long clkgena_divmux_recalc_rate(struct clk_hw *hw, 16994885fafSGabriel FERNANDEZ unsigned long parent_rate) 17094885fafSGabriel FERNANDEZ { 17194885fafSGabriel FERNANDEZ struct clkgena_divmux *genamux = to_clkgena_divmux(hw); 17294885fafSGabriel FERNANDEZ struct clk_hw *div_hw = &genamux->div[genamux->muxsel].hw; 17394885fafSGabriel FERNANDEZ 1744e907ef6SJavier Martinez Canillas __clk_hw_set_clk(div_hw, hw); 17594885fafSGabriel FERNANDEZ 17694885fafSGabriel FERNANDEZ return clk_divider_ops.recalc_rate(div_hw, parent_rate); 17794885fafSGabriel FERNANDEZ } 17894885fafSGabriel FERNANDEZ 17994885fafSGabriel FERNANDEZ static int clkgena_divmux_set_rate(struct clk_hw *hw, unsigned long rate, 18094885fafSGabriel FERNANDEZ unsigned long parent_rate) 18194885fafSGabriel FERNANDEZ { 18294885fafSGabriel FERNANDEZ struct clkgena_divmux *genamux = to_clkgena_divmux(hw); 18394885fafSGabriel FERNANDEZ struct clk_hw *div_hw = &genamux->div[genamux->muxsel].hw; 18494885fafSGabriel FERNANDEZ 1854e907ef6SJavier Martinez Canillas __clk_hw_set_clk(div_hw, hw); 18694885fafSGabriel FERNANDEZ 18794885fafSGabriel FERNANDEZ return clk_divider_ops.set_rate(div_hw, rate, parent_rate); 18894885fafSGabriel FERNANDEZ } 18994885fafSGabriel FERNANDEZ 19094885fafSGabriel FERNANDEZ static long clkgena_divmux_round_rate(struct clk_hw *hw, unsigned long rate, 19194885fafSGabriel FERNANDEZ unsigned long *prate) 19294885fafSGabriel FERNANDEZ { 19394885fafSGabriel FERNANDEZ struct clkgena_divmux *genamux = to_clkgena_divmux(hw); 19494885fafSGabriel FERNANDEZ struct clk_hw *div_hw = &genamux->div[genamux->muxsel].hw; 19594885fafSGabriel FERNANDEZ 1964e907ef6SJavier Martinez Canillas __clk_hw_set_clk(div_hw, hw); 19794885fafSGabriel FERNANDEZ 19894885fafSGabriel FERNANDEZ return clk_divider_ops.round_rate(div_hw, rate, prate); 19994885fafSGabriel FERNANDEZ } 20094885fafSGabriel FERNANDEZ 20194885fafSGabriel FERNANDEZ static const struct clk_ops clkgena_divmux_ops = { 20294885fafSGabriel FERNANDEZ .enable = clkgena_divmux_enable, 20394885fafSGabriel FERNANDEZ .disable = clkgena_divmux_disable, 20494885fafSGabriel FERNANDEZ .is_enabled = clkgena_divmux_is_enabled, 20594885fafSGabriel FERNANDEZ .get_parent = clkgena_divmux_get_parent, 20694885fafSGabriel FERNANDEZ .set_parent = clkgena_divmux_set_parent, 20794885fafSGabriel FERNANDEZ .round_rate = clkgena_divmux_round_rate, 20894885fafSGabriel FERNANDEZ .recalc_rate = clkgena_divmux_recalc_rate, 20994885fafSGabriel FERNANDEZ .set_rate = clkgena_divmux_set_rate, 21094885fafSGabriel FERNANDEZ }; 21194885fafSGabriel FERNANDEZ 21294885fafSGabriel FERNANDEZ /** 21394885fafSGabriel FERNANDEZ * clk_register_genamux - register a genamux clock with the clock framework 21494885fafSGabriel FERNANDEZ */ 21586665d28SStephen Boyd static struct clk * __init clk_register_genamux(const char *name, 21694885fafSGabriel FERNANDEZ const char **parent_names, u8 num_parents, 21794885fafSGabriel FERNANDEZ void __iomem *reg, 21894885fafSGabriel FERNANDEZ const struct clkgena_divmux_data *muxdata, 21994885fafSGabriel FERNANDEZ u32 idx) 22094885fafSGabriel FERNANDEZ { 22194885fafSGabriel FERNANDEZ /* 22294885fafSGabriel FERNANDEZ * Fixed constants across all ClockgenA variants 22394885fafSGabriel FERNANDEZ */ 22494885fafSGabriel FERNANDEZ const int mux_width = 2; 22594885fafSGabriel FERNANDEZ const int divider_width = 5; 22694885fafSGabriel FERNANDEZ struct clkgena_divmux *genamux; 22794885fafSGabriel FERNANDEZ struct clk *clk; 22894885fafSGabriel FERNANDEZ struct clk_init_data init; 22994885fafSGabriel FERNANDEZ int i; 23094885fafSGabriel FERNANDEZ 23194885fafSGabriel FERNANDEZ genamux = kzalloc(sizeof(*genamux), GFP_KERNEL); 23294885fafSGabriel FERNANDEZ if (!genamux) 23394885fafSGabriel FERNANDEZ return ERR_PTR(-ENOMEM); 23494885fafSGabriel FERNANDEZ 23594885fafSGabriel FERNANDEZ init.name = name; 23694885fafSGabriel FERNANDEZ init.ops = &clkgena_divmux_ops; 23718fee453SPankaj Dev init.flags = CLK_IS_BASIC | CLK_GET_RATE_NOCACHE; 23894885fafSGabriel FERNANDEZ init.parent_names = parent_names; 23994885fafSGabriel FERNANDEZ init.num_parents = num_parents; 24094885fafSGabriel FERNANDEZ 24194885fafSGabriel FERNANDEZ genamux->mux.lock = &clkgena_divmux_lock; 24294885fafSGabriel FERNANDEZ genamux->mux.mask = BIT(mux_width) - 1; 24394885fafSGabriel FERNANDEZ genamux->mux.shift = muxdata->mux_start_bit + (idx * mux_width); 24494885fafSGabriel FERNANDEZ if (genamux->mux.shift > 31) { 24594885fafSGabriel FERNANDEZ /* 24694885fafSGabriel FERNANDEZ * We have spilled into the second mux register so 24794885fafSGabriel FERNANDEZ * adjust the register address and the bit shift accordingly 24894885fafSGabriel FERNANDEZ */ 24994885fafSGabriel FERNANDEZ genamux->mux.reg = reg + muxdata->mux_offset2; 25094885fafSGabriel FERNANDEZ genamux->mux.shift -= 32; 25194885fafSGabriel FERNANDEZ } else { 25294885fafSGabriel FERNANDEZ genamux->mux.reg = reg + muxdata->mux_offset; 25394885fafSGabriel FERNANDEZ } 25494885fafSGabriel FERNANDEZ 25594885fafSGabriel FERNANDEZ for (i = 0; i < NUM_INPUTS; i++) { 25694885fafSGabriel FERNANDEZ /* 25794885fafSGabriel FERNANDEZ * Divider config for each input 25894885fafSGabriel FERNANDEZ */ 25994885fafSGabriel FERNANDEZ void __iomem *divbase = reg + muxdata->div_offsets[i]; 26094885fafSGabriel FERNANDEZ genamux->div[i].width = divider_width; 26194885fafSGabriel FERNANDEZ genamux->div[i].reg = divbase + (idx * sizeof(u32)); 26294885fafSGabriel FERNANDEZ 26394885fafSGabriel FERNANDEZ /* 26494885fafSGabriel FERNANDEZ * Mux enabled/running feedback register for each input. 26594885fafSGabriel FERNANDEZ */ 26694885fafSGabriel FERNANDEZ genamux->feedback_reg[i] = reg + muxdata->fb_offsets[i]; 26794885fafSGabriel FERNANDEZ } 26894885fafSGabriel FERNANDEZ 26994885fafSGabriel FERNANDEZ genamux->feedback_bit_idx = muxdata->fb_start_bit_idx + idx; 27094885fafSGabriel FERNANDEZ genamux->hw.init = &init; 27194885fafSGabriel FERNANDEZ 27294885fafSGabriel FERNANDEZ clk = clk_register(NULL, &genamux->hw); 27394885fafSGabriel FERNANDEZ if (IS_ERR(clk)) { 27494885fafSGabriel FERNANDEZ kfree(genamux); 27594885fafSGabriel FERNANDEZ goto err; 27694885fafSGabriel FERNANDEZ } 27794885fafSGabriel FERNANDEZ 27894885fafSGabriel FERNANDEZ pr_debug("%s: parent %s rate %lu\n", 27994885fafSGabriel FERNANDEZ __clk_get_name(clk), 28094885fafSGabriel FERNANDEZ __clk_get_name(clk_get_parent(clk)), 28194885fafSGabriel FERNANDEZ clk_get_rate(clk)); 28294885fafSGabriel FERNANDEZ err: 28394885fafSGabriel FERNANDEZ return clk; 28494885fafSGabriel FERNANDEZ } 28594885fafSGabriel FERNANDEZ 28694885fafSGabriel FERNANDEZ static struct clkgena_divmux_data st_divmux_c65hs = { 28794885fafSGabriel FERNANDEZ .num_outputs = 4, 28894885fafSGabriel FERNANDEZ .mux_offset = 0x14, 28994885fafSGabriel FERNANDEZ .mux_start_bit = 0, 29094885fafSGabriel FERNANDEZ .div_offsets = { 0x800, 0x900, 0xb00 }, 29194885fafSGabriel FERNANDEZ .fb_offsets = { 0x18, 0x1c, 0x20 }, 29294885fafSGabriel FERNANDEZ .fb_start_bit_idx = 0, 29394885fafSGabriel FERNANDEZ }; 29494885fafSGabriel FERNANDEZ 29594885fafSGabriel FERNANDEZ static struct clkgena_divmux_data st_divmux_c65ls = { 29694885fafSGabriel FERNANDEZ .num_outputs = 14, 29794885fafSGabriel FERNANDEZ .mux_offset = 0x14, 29894885fafSGabriel FERNANDEZ .mux_offset2 = 0x24, 29994885fafSGabriel FERNANDEZ .mux_start_bit = 8, 30094885fafSGabriel FERNANDEZ .div_offsets = { 0x810, 0xa10, 0xb10 }, 30194885fafSGabriel FERNANDEZ .fb_offsets = { 0x18, 0x1c, 0x20 }, 30294885fafSGabriel FERNANDEZ .fb_start_bit_idx = 4, 30394885fafSGabriel FERNANDEZ }; 30494885fafSGabriel FERNANDEZ 30594885fafSGabriel FERNANDEZ static struct clkgena_divmux_data st_divmux_c32odf0 = { 30694885fafSGabriel FERNANDEZ .num_outputs = 8, 30794885fafSGabriel FERNANDEZ .mux_offset = 0x1c, 30894885fafSGabriel FERNANDEZ .mux_start_bit = 0, 30994885fafSGabriel FERNANDEZ .div_offsets = { 0x800, 0x900, 0xa60 }, 31094885fafSGabriel FERNANDEZ .fb_offsets = { 0x2c, 0x24, 0x28 }, 31194885fafSGabriel FERNANDEZ .fb_start_bit_idx = 0, 31294885fafSGabriel FERNANDEZ }; 31394885fafSGabriel FERNANDEZ 31494885fafSGabriel FERNANDEZ static struct clkgena_divmux_data st_divmux_c32odf1 = { 31594885fafSGabriel FERNANDEZ .num_outputs = 8, 31694885fafSGabriel FERNANDEZ .mux_offset = 0x1c, 31794885fafSGabriel FERNANDEZ .mux_start_bit = 16, 31894885fafSGabriel FERNANDEZ .div_offsets = { 0x820, 0x980, 0xa80 }, 31994885fafSGabriel FERNANDEZ .fb_offsets = { 0x2c, 0x24, 0x28 }, 32094885fafSGabriel FERNANDEZ .fb_start_bit_idx = 8, 32194885fafSGabriel FERNANDEZ }; 32294885fafSGabriel FERNANDEZ 32394885fafSGabriel FERNANDEZ static struct clkgena_divmux_data st_divmux_c32odf2 = { 32494885fafSGabriel FERNANDEZ .num_outputs = 8, 32594885fafSGabriel FERNANDEZ .mux_offset = 0x20, 32694885fafSGabriel FERNANDEZ .mux_start_bit = 0, 32794885fafSGabriel FERNANDEZ .div_offsets = { 0x840, 0xa20, 0xb10 }, 32894885fafSGabriel FERNANDEZ .fb_offsets = { 0x2c, 0x24, 0x28 }, 32994885fafSGabriel FERNANDEZ .fb_start_bit_idx = 16, 33094885fafSGabriel FERNANDEZ }; 33194885fafSGabriel FERNANDEZ 33294885fafSGabriel FERNANDEZ static struct clkgena_divmux_data st_divmux_c32odf3 = { 33394885fafSGabriel FERNANDEZ .num_outputs = 8, 33494885fafSGabriel FERNANDEZ .mux_offset = 0x20, 33594885fafSGabriel FERNANDEZ .mux_start_bit = 16, 33694885fafSGabriel FERNANDEZ .div_offsets = { 0x860, 0xa40, 0xb30 }, 33794885fafSGabriel FERNANDEZ .fb_offsets = { 0x2c, 0x24, 0x28 }, 33894885fafSGabriel FERNANDEZ .fb_start_bit_idx = 24, 33994885fafSGabriel FERNANDEZ }; 34094885fafSGabriel FERNANDEZ 341f375573cSFabian Frederick static const struct of_device_id clkgena_divmux_of_match[] = { 34294885fafSGabriel FERNANDEZ { 34394885fafSGabriel FERNANDEZ .compatible = "st,clkgena-divmux-c65-hs", 34494885fafSGabriel FERNANDEZ .data = &st_divmux_c65hs, 34594885fafSGabriel FERNANDEZ }, 34694885fafSGabriel FERNANDEZ { 34794885fafSGabriel FERNANDEZ .compatible = "st,clkgena-divmux-c65-ls", 34894885fafSGabriel FERNANDEZ .data = &st_divmux_c65ls, 34994885fafSGabriel FERNANDEZ }, 35094885fafSGabriel FERNANDEZ { 35194885fafSGabriel FERNANDEZ .compatible = "st,clkgena-divmux-c32-odf0", 35294885fafSGabriel FERNANDEZ .data = &st_divmux_c32odf0, 35394885fafSGabriel FERNANDEZ }, 35494885fafSGabriel FERNANDEZ { 35594885fafSGabriel FERNANDEZ .compatible = "st,clkgena-divmux-c32-odf1", 35694885fafSGabriel FERNANDEZ .data = &st_divmux_c32odf1, 35794885fafSGabriel FERNANDEZ }, 35894885fafSGabriel FERNANDEZ { 35994885fafSGabriel FERNANDEZ .compatible = "st,clkgena-divmux-c32-odf2", 36094885fafSGabriel FERNANDEZ .data = &st_divmux_c32odf2, 36194885fafSGabriel FERNANDEZ }, 36294885fafSGabriel FERNANDEZ { 36394885fafSGabriel FERNANDEZ .compatible = "st,clkgena-divmux-c32-odf3", 36494885fafSGabriel FERNANDEZ .data = &st_divmux_c32odf3, 36594885fafSGabriel FERNANDEZ }, 36694885fafSGabriel FERNANDEZ {} 36794885fafSGabriel FERNANDEZ }; 36894885fafSGabriel FERNANDEZ 36986665d28SStephen Boyd static void __iomem * __init clkgen_get_register_base(struct device_node *np) 37094885fafSGabriel FERNANDEZ { 37194885fafSGabriel FERNANDEZ struct device_node *pnode; 37286665d28SStephen Boyd void __iomem *reg; 37394885fafSGabriel FERNANDEZ 37494885fafSGabriel FERNANDEZ pnode = of_get_parent(np); 37594885fafSGabriel FERNANDEZ if (!pnode) 37694885fafSGabriel FERNANDEZ return NULL; 37794885fafSGabriel FERNANDEZ 37894885fafSGabriel FERNANDEZ reg = of_iomap(pnode, 0); 37994885fafSGabriel FERNANDEZ 38094885fafSGabriel FERNANDEZ of_node_put(pnode); 38194885fafSGabriel FERNANDEZ return reg; 38294885fafSGabriel FERNANDEZ } 38394885fafSGabriel FERNANDEZ 3848e6dd77cSStephen Boyd static void __init st_of_clkgena_divmux_setup(struct device_node *np) 38594885fafSGabriel FERNANDEZ { 38694885fafSGabriel FERNANDEZ const struct of_device_id *match; 38794885fafSGabriel FERNANDEZ const struct clkgena_divmux_data *data; 38894885fafSGabriel FERNANDEZ struct clk_onecell_data *clk_data; 38994885fafSGabriel FERNANDEZ void __iomem *reg; 39094885fafSGabriel FERNANDEZ const char **parents; 39194885fafSGabriel FERNANDEZ int num_parents = 0, i; 39294885fafSGabriel FERNANDEZ 39394885fafSGabriel FERNANDEZ match = of_match_node(clkgena_divmux_of_match, np); 39494885fafSGabriel FERNANDEZ if (WARN_ON(!match)) 39594885fafSGabriel FERNANDEZ return; 39694885fafSGabriel FERNANDEZ 39786665d28SStephen Boyd data = match->data; 39894885fafSGabriel FERNANDEZ 39994885fafSGabriel FERNANDEZ reg = clkgen_get_register_base(np); 40094885fafSGabriel FERNANDEZ if (!reg) 40194885fafSGabriel FERNANDEZ return; 40294885fafSGabriel FERNANDEZ 40394885fafSGabriel FERNANDEZ parents = clkgen_mux_get_parents(np, &num_parents); 40494885fafSGabriel FERNANDEZ if (IS_ERR(parents)) 40586665d28SStephen Boyd goto err_parents; 40694885fafSGabriel FERNANDEZ 40794885fafSGabriel FERNANDEZ clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL); 40894885fafSGabriel FERNANDEZ if (!clk_data) 40986665d28SStephen Boyd goto err_alloc; 41094885fafSGabriel FERNANDEZ 41194885fafSGabriel FERNANDEZ clk_data->clk_num = data->num_outputs; 41286665d28SStephen Boyd clk_data->clks = kcalloc(clk_data->clk_num, sizeof(struct clk *), 41394885fafSGabriel FERNANDEZ GFP_KERNEL); 41494885fafSGabriel FERNANDEZ 41594885fafSGabriel FERNANDEZ if (!clk_data->clks) 41686665d28SStephen Boyd goto err_alloc_clks; 41794885fafSGabriel FERNANDEZ 41894885fafSGabriel FERNANDEZ for (i = 0; i < clk_data->clk_num; i++) { 41994885fafSGabriel FERNANDEZ struct clk *clk; 42094885fafSGabriel FERNANDEZ const char *clk_name; 42194885fafSGabriel FERNANDEZ 42294885fafSGabriel FERNANDEZ if (of_property_read_string_index(np, "clock-output-names", 42394885fafSGabriel FERNANDEZ i, &clk_name)) 42494885fafSGabriel FERNANDEZ break; 42594885fafSGabriel FERNANDEZ 42694885fafSGabriel FERNANDEZ /* 42794885fafSGabriel FERNANDEZ * If we read an empty clock name then the output is unused 42894885fafSGabriel FERNANDEZ */ 42994885fafSGabriel FERNANDEZ if (*clk_name == '\0') 43094885fafSGabriel FERNANDEZ continue; 43194885fafSGabriel FERNANDEZ 43294885fafSGabriel FERNANDEZ clk = clk_register_genamux(clk_name, parents, num_parents, 43394885fafSGabriel FERNANDEZ reg, data, i); 43494885fafSGabriel FERNANDEZ 43594885fafSGabriel FERNANDEZ if (IS_ERR(clk)) 43694885fafSGabriel FERNANDEZ goto err; 43794885fafSGabriel FERNANDEZ 43894885fafSGabriel FERNANDEZ clk_data->clks[i] = clk; 43994885fafSGabriel FERNANDEZ } 44094885fafSGabriel FERNANDEZ 44194885fafSGabriel FERNANDEZ kfree(parents); 44294885fafSGabriel FERNANDEZ 44394885fafSGabriel FERNANDEZ of_clk_add_provider(np, of_clk_src_onecell_get, clk_data); 44494885fafSGabriel FERNANDEZ return; 44594885fafSGabriel FERNANDEZ err: 44694885fafSGabriel FERNANDEZ kfree(clk_data->clks); 44786665d28SStephen Boyd err_alloc_clks: 44894885fafSGabriel FERNANDEZ kfree(clk_data); 44986665d28SStephen Boyd err_alloc: 45094885fafSGabriel FERNANDEZ kfree(parents); 45186665d28SStephen Boyd err_parents: 45286665d28SStephen Boyd iounmap(reg); 45394885fafSGabriel FERNANDEZ } 45494885fafSGabriel FERNANDEZ CLK_OF_DECLARE(clkgenadivmux, "st,clkgena-divmux", st_of_clkgena_divmux_setup); 45594885fafSGabriel FERNANDEZ 45694885fafSGabriel FERNANDEZ struct clkgena_prediv_data { 45794885fafSGabriel FERNANDEZ u32 offset; 45894885fafSGabriel FERNANDEZ u8 shift; 45994885fafSGabriel FERNANDEZ struct clk_div_table *table; 46094885fafSGabriel FERNANDEZ }; 46194885fafSGabriel FERNANDEZ 46294885fafSGabriel FERNANDEZ static struct clk_div_table prediv_table16[] = { 46394885fafSGabriel FERNANDEZ { .val = 0, .div = 1 }, 46494885fafSGabriel FERNANDEZ { .val = 1, .div = 16 }, 46594885fafSGabriel FERNANDEZ { .div = 0 }, 46694885fafSGabriel FERNANDEZ }; 46794885fafSGabriel FERNANDEZ 46894885fafSGabriel FERNANDEZ static struct clkgena_prediv_data prediv_c65_data = { 46994885fafSGabriel FERNANDEZ .offset = 0x4c, 47094885fafSGabriel FERNANDEZ .shift = 31, 47194885fafSGabriel FERNANDEZ .table = prediv_table16, 47294885fafSGabriel FERNANDEZ }; 47394885fafSGabriel FERNANDEZ 47494885fafSGabriel FERNANDEZ static struct clkgena_prediv_data prediv_c32_data = { 47594885fafSGabriel FERNANDEZ .offset = 0x50, 47694885fafSGabriel FERNANDEZ .shift = 1, 47794885fafSGabriel FERNANDEZ .table = prediv_table16, 47894885fafSGabriel FERNANDEZ }; 47994885fafSGabriel FERNANDEZ 480f375573cSFabian Frederick static const struct of_device_id clkgena_prediv_of_match[] = { 48194885fafSGabriel FERNANDEZ { .compatible = "st,clkgena-prediv-c65", .data = &prediv_c65_data }, 48294885fafSGabriel FERNANDEZ { .compatible = "st,clkgena-prediv-c32", .data = &prediv_c32_data }, 48394885fafSGabriel FERNANDEZ {} 48494885fafSGabriel FERNANDEZ }; 48594885fafSGabriel FERNANDEZ 4868e6dd77cSStephen Boyd static void __init st_of_clkgena_prediv_setup(struct device_node *np) 48794885fafSGabriel FERNANDEZ { 48894885fafSGabriel FERNANDEZ const struct of_device_id *match; 48994885fafSGabriel FERNANDEZ void __iomem *reg; 49094885fafSGabriel FERNANDEZ const char *parent_name, *clk_name; 49194885fafSGabriel FERNANDEZ struct clk *clk; 49286665d28SStephen Boyd const struct clkgena_prediv_data *data; 49394885fafSGabriel FERNANDEZ 49494885fafSGabriel FERNANDEZ match = of_match_node(clkgena_prediv_of_match, np); 49594885fafSGabriel FERNANDEZ if (!match) { 49694885fafSGabriel FERNANDEZ pr_err("%s: No matching data\n", __func__); 49794885fafSGabriel FERNANDEZ return; 49894885fafSGabriel FERNANDEZ } 49994885fafSGabriel FERNANDEZ 50086665d28SStephen Boyd data = match->data; 50194885fafSGabriel FERNANDEZ 50294885fafSGabriel FERNANDEZ reg = clkgen_get_register_base(np); 50394885fafSGabriel FERNANDEZ if (!reg) 50494885fafSGabriel FERNANDEZ return; 50594885fafSGabriel FERNANDEZ 50694885fafSGabriel FERNANDEZ parent_name = of_clk_get_parent_name(np, 0); 50794885fafSGabriel FERNANDEZ if (!parent_name) 50886665d28SStephen Boyd goto err; 50994885fafSGabriel FERNANDEZ 51094885fafSGabriel FERNANDEZ if (of_property_read_string_index(np, "clock-output-names", 51194885fafSGabriel FERNANDEZ 0, &clk_name)) 51286665d28SStephen Boyd goto err; 51394885fafSGabriel FERNANDEZ 51418fee453SPankaj Dev clk = clk_register_divider_table(NULL, clk_name, parent_name, 51518fee453SPankaj Dev CLK_GET_RATE_NOCACHE, 51694885fafSGabriel FERNANDEZ reg + data->offset, data->shift, 1, 51794885fafSGabriel FERNANDEZ 0, data->table, NULL); 51894885fafSGabriel FERNANDEZ if (IS_ERR(clk)) 51986665d28SStephen Boyd goto err; 52094885fafSGabriel FERNANDEZ 52194885fafSGabriel FERNANDEZ of_clk_add_provider(np, of_clk_src_simple_get, clk); 52294885fafSGabriel FERNANDEZ pr_debug("%s: parent %s rate %u\n", 52394885fafSGabriel FERNANDEZ __clk_get_name(clk), 52494885fafSGabriel FERNANDEZ __clk_get_name(clk_get_parent(clk)), 52594885fafSGabriel FERNANDEZ (unsigned int)clk_get_rate(clk)); 52694885fafSGabriel FERNANDEZ 52794885fafSGabriel FERNANDEZ return; 52886665d28SStephen Boyd err: 52986665d28SStephen Boyd iounmap(reg); 53094885fafSGabriel FERNANDEZ } 53194885fafSGabriel FERNANDEZ CLK_OF_DECLARE(clkgenaprediv, "st,clkgena-prediv", st_of_clkgena_prediv_setup); 53244993d38SGabriel FERNANDEZ 53344993d38SGabriel FERNANDEZ struct clkgen_mux_data { 53444993d38SGabriel FERNANDEZ u32 offset; 53544993d38SGabriel FERNANDEZ u8 shift; 53644993d38SGabriel FERNANDEZ u8 width; 53744993d38SGabriel FERNANDEZ spinlock_t *lock; 53844993d38SGabriel FERNANDEZ unsigned long clk_flags; 53944993d38SGabriel FERNANDEZ u8 mux_flags; 54044993d38SGabriel FERNANDEZ }; 54144993d38SGabriel FERNANDEZ 54244993d38SGabriel FERNANDEZ static struct clkgen_mux_data clkgen_mux_c_vcc_hd_416 = { 54344993d38SGabriel FERNANDEZ .offset = 0, 54444993d38SGabriel FERNANDEZ .shift = 0, 54544993d38SGabriel FERNANDEZ .width = 1, 54644993d38SGabriel FERNANDEZ }; 54744993d38SGabriel FERNANDEZ 54844993d38SGabriel FERNANDEZ static struct clkgen_mux_data clkgen_mux_f_vcc_fvdp_416 = { 54944993d38SGabriel FERNANDEZ .offset = 0, 55044993d38SGabriel FERNANDEZ .shift = 0, 55144993d38SGabriel FERNANDEZ .width = 1, 55244993d38SGabriel FERNANDEZ }; 55344993d38SGabriel FERNANDEZ 55444993d38SGabriel FERNANDEZ static struct clkgen_mux_data clkgen_mux_f_vcc_hva_416 = { 55544993d38SGabriel FERNANDEZ .offset = 0, 55644993d38SGabriel FERNANDEZ .shift = 0, 55744993d38SGabriel FERNANDEZ .width = 1, 55844993d38SGabriel FERNANDEZ }; 55944993d38SGabriel FERNANDEZ 56044993d38SGabriel FERNANDEZ static struct clkgen_mux_data clkgen_mux_f_vcc_hd_416 = { 56144993d38SGabriel FERNANDEZ .offset = 0, 56244993d38SGabriel FERNANDEZ .shift = 16, 56344993d38SGabriel FERNANDEZ .width = 1, 56444993d38SGabriel FERNANDEZ .lock = &clkgenf_lock, 56544993d38SGabriel FERNANDEZ }; 56644993d38SGabriel FERNANDEZ 56744993d38SGabriel FERNANDEZ static struct clkgen_mux_data clkgen_mux_c_vcc_sd_416 = { 56844993d38SGabriel FERNANDEZ .offset = 0, 56944993d38SGabriel FERNANDEZ .shift = 17, 57044993d38SGabriel FERNANDEZ .width = 1, 57144993d38SGabriel FERNANDEZ .lock = &clkgenf_lock, 57244993d38SGabriel FERNANDEZ }; 57344993d38SGabriel FERNANDEZ 574ab35dc13SGabriel FERNANDEZ static struct clkgen_mux_data stih415_a9_mux_data = { 575ab35dc13SGabriel FERNANDEZ .offset = 0, 576ab35dc13SGabriel FERNANDEZ .shift = 1, 577ab35dc13SGabriel FERNANDEZ .width = 2, 578ab35dc13SGabriel FERNANDEZ }; 579ab35dc13SGabriel FERNANDEZ static struct clkgen_mux_data stih416_a9_mux_data = { 580ab35dc13SGabriel FERNANDEZ .offset = 0, 581ab35dc13SGabriel FERNANDEZ .shift = 0, 582ab35dc13SGabriel FERNANDEZ .width = 2, 583ab35dc13SGabriel FERNANDEZ }; 58413e6f2daSGabriel FERNANDEZ static struct clkgen_mux_data stih407_a9_mux_data = { 58513e6f2daSGabriel FERNANDEZ .offset = 0x1a4, 5863be6d8ceSGabriel Fernandez .shift = 0, 58713e6f2daSGabriel FERNANDEZ .width = 2, 58813e6f2daSGabriel FERNANDEZ }; 589ab35dc13SGabriel FERNANDEZ 590f375573cSFabian Frederick static const struct of_device_id mux_of_match[] = { 59144993d38SGabriel FERNANDEZ { 59244993d38SGabriel FERNANDEZ .compatible = "st,stih416-clkgenc-vcc-hd", 59344993d38SGabriel FERNANDEZ .data = &clkgen_mux_c_vcc_hd_416, 59444993d38SGabriel FERNANDEZ }, 59544993d38SGabriel FERNANDEZ { 59644993d38SGabriel FERNANDEZ .compatible = "st,stih416-clkgenf-vcc-fvdp", 59744993d38SGabriel FERNANDEZ .data = &clkgen_mux_f_vcc_fvdp_416, 59844993d38SGabriel FERNANDEZ }, 59944993d38SGabriel FERNANDEZ { 60044993d38SGabriel FERNANDEZ .compatible = "st,stih416-clkgenf-vcc-hva", 60144993d38SGabriel FERNANDEZ .data = &clkgen_mux_f_vcc_hva_416, 60244993d38SGabriel FERNANDEZ }, 60344993d38SGabriel FERNANDEZ { 60444993d38SGabriel FERNANDEZ .compatible = "st,stih416-clkgenf-vcc-hd", 60544993d38SGabriel FERNANDEZ .data = &clkgen_mux_f_vcc_hd_416, 60644993d38SGabriel FERNANDEZ }, 60744993d38SGabriel FERNANDEZ { 60844993d38SGabriel FERNANDEZ .compatible = "st,stih416-clkgenf-vcc-sd", 60944993d38SGabriel FERNANDEZ .data = &clkgen_mux_c_vcc_sd_416, 61044993d38SGabriel FERNANDEZ }, 611ab35dc13SGabriel FERNANDEZ { 612ab35dc13SGabriel FERNANDEZ .compatible = "st,stih415-clkgen-a9-mux", 613ab35dc13SGabriel FERNANDEZ .data = &stih415_a9_mux_data, 614ab35dc13SGabriel FERNANDEZ }, 615ab35dc13SGabriel FERNANDEZ { 616ab35dc13SGabriel FERNANDEZ .compatible = "st,stih416-clkgen-a9-mux", 617ab35dc13SGabriel FERNANDEZ .data = &stih416_a9_mux_data, 618ab35dc13SGabriel FERNANDEZ }, 61913e6f2daSGabriel FERNANDEZ { 62013e6f2daSGabriel FERNANDEZ .compatible = "st,stih407-clkgen-a9-mux", 62113e6f2daSGabriel FERNANDEZ .data = &stih407_a9_mux_data, 62213e6f2daSGabriel FERNANDEZ }, 62344993d38SGabriel FERNANDEZ {} 62444993d38SGabriel FERNANDEZ }; 62544993d38SGabriel FERNANDEZ 6268e6dd77cSStephen Boyd static void __init st_of_clkgen_mux_setup(struct device_node *np) 62744993d38SGabriel FERNANDEZ { 62844993d38SGabriel FERNANDEZ const struct of_device_id *match; 62944993d38SGabriel FERNANDEZ struct clk *clk; 63044993d38SGabriel FERNANDEZ void __iomem *reg; 63144993d38SGabriel FERNANDEZ const char **parents; 63244993d38SGabriel FERNANDEZ int num_parents; 63386665d28SStephen Boyd const struct clkgen_mux_data *data; 63444993d38SGabriel FERNANDEZ 63544993d38SGabriel FERNANDEZ match = of_match_node(mux_of_match, np); 63644993d38SGabriel FERNANDEZ if (!match) { 63744993d38SGabriel FERNANDEZ pr_err("%s: No matching data\n", __func__); 63844993d38SGabriel FERNANDEZ return; 63944993d38SGabriel FERNANDEZ } 64044993d38SGabriel FERNANDEZ 64186665d28SStephen Boyd data = match->data; 64244993d38SGabriel FERNANDEZ 64344993d38SGabriel FERNANDEZ reg = of_iomap(np, 0); 64444993d38SGabriel FERNANDEZ if (!reg) { 64544993d38SGabriel FERNANDEZ pr_err("%s: Failed to get base address\n", __func__); 64644993d38SGabriel FERNANDEZ return; 64744993d38SGabriel FERNANDEZ } 64844993d38SGabriel FERNANDEZ 64944993d38SGabriel FERNANDEZ parents = clkgen_mux_get_parents(np, &num_parents); 65044993d38SGabriel FERNANDEZ if (IS_ERR(parents)) { 65144993d38SGabriel FERNANDEZ pr_err("%s: Failed to get parents (%ld)\n", 65244993d38SGabriel FERNANDEZ __func__, PTR_ERR(parents)); 65386665d28SStephen Boyd goto err_parents; 65444993d38SGabriel FERNANDEZ } 65544993d38SGabriel FERNANDEZ 65644993d38SGabriel FERNANDEZ clk = clk_register_mux(NULL, np->name, parents, num_parents, 65744993d38SGabriel FERNANDEZ data->clk_flags | CLK_SET_RATE_PARENT, 65844993d38SGabriel FERNANDEZ reg + data->offset, 65944993d38SGabriel FERNANDEZ data->shift, data->width, data->mux_flags, 66044993d38SGabriel FERNANDEZ data->lock); 66144993d38SGabriel FERNANDEZ if (IS_ERR(clk)) 66244993d38SGabriel FERNANDEZ goto err; 66344993d38SGabriel FERNANDEZ 66444993d38SGabriel FERNANDEZ pr_debug("%s: parent %s rate %u\n", 66544993d38SGabriel FERNANDEZ __clk_get_name(clk), 66644993d38SGabriel FERNANDEZ __clk_get_name(clk_get_parent(clk)), 66744993d38SGabriel FERNANDEZ (unsigned int)clk_get_rate(clk)); 66844993d38SGabriel FERNANDEZ 66986665d28SStephen Boyd kfree(parents); 67044993d38SGabriel FERNANDEZ of_clk_add_provider(np, of_clk_src_simple_get, clk); 67186665d28SStephen Boyd return; 67244993d38SGabriel FERNANDEZ 67344993d38SGabriel FERNANDEZ err: 67444993d38SGabriel FERNANDEZ kfree(parents); 67586665d28SStephen Boyd err_parents: 67686665d28SStephen Boyd iounmap(reg); 67744993d38SGabriel FERNANDEZ } 67844993d38SGabriel FERNANDEZ CLK_OF_DECLARE(clkgen_mux, "st,clkgen-mux", st_of_clkgen_mux_setup); 67944993d38SGabriel FERNANDEZ 68044993d38SGabriel FERNANDEZ #define VCC_MAX_CHANNELS 16 68144993d38SGabriel FERNANDEZ 68244993d38SGabriel FERNANDEZ #define VCC_GATE_OFFSET 0x0 68344993d38SGabriel FERNANDEZ #define VCC_MUX_OFFSET 0x4 68444993d38SGabriel FERNANDEZ #define VCC_DIV_OFFSET 0x8 68544993d38SGabriel FERNANDEZ 68644993d38SGabriel FERNANDEZ struct clkgen_vcc_data { 68744993d38SGabriel FERNANDEZ spinlock_t *lock; 68844993d38SGabriel FERNANDEZ unsigned long clk_flags; 68944993d38SGabriel FERNANDEZ }; 69044993d38SGabriel FERNANDEZ 69144993d38SGabriel FERNANDEZ static struct clkgen_vcc_data st_clkgenc_vcc_416 = { 69244993d38SGabriel FERNANDEZ .clk_flags = CLK_SET_RATE_PARENT, 69344993d38SGabriel FERNANDEZ }; 69444993d38SGabriel FERNANDEZ 69544993d38SGabriel FERNANDEZ static struct clkgen_vcc_data st_clkgenf_vcc_416 = { 69644993d38SGabriel FERNANDEZ .lock = &clkgenf_lock, 69744993d38SGabriel FERNANDEZ }; 69844993d38SGabriel FERNANDEZ 699f375573cSFabian Frederick static const struct of_device_id vcc_of_match[] = { 70044993d38SGabriel FERNANDEZ { .compatible = "st,stih416-clkgenc", .data = &st_clkgenc_vcc_416 }, 70144993d38SGabriel FERNANDEZ { .compatible = "st,stih416-clkgenf", .data = &st_clkgenf_vcc_416 }, 70244993d38SGabriel FERNANDEZ {} 70344993d38SGabriel FERNANDEZ }; 70444993d38SGabriel FERNANDEZ 7058e6dd77cSStephen Boyd static void __init st_of_clkgen_vcc_setup(struct device_node *np) 70644993d38SGabriel FERNANDEZ { 70744993d38SGabriel FERNANDEZ const struct of_device_id *match; 70844993d38SGabriel FERNANDEZ void __iomem *reg; 70944993d38SGabriel FERNANDEZ const char **parents; 71044993d38SGabriel FERNANDEZ int num_parents, i; 71144993d38SGabriel FERNANDEZ struct clk_onecell_data *clk_data; 71286665d28SStephen Boyd const struct clkgen_vcc_data *data; 71344993d38SGabriel FERNANDEZ 71444993d38SGabriel FERNANDEZ match = of_match_node(vcc_of_match, np); 71544993d38SGabriel FERNANDEZ if (WARN_ON(!match)) 71644993d38SGabriel FERNANDEZ return; 71786665d28SStephen Boyd data = match->data; 71844993d38SGabriel FERNANDEZ 71944993d38SGabriel FERNANDEZ reg = of_iomap(np, 0); 72044993d38SGabriel FERNANDEZ if (!reg) 72144993d38SGabriel FERNANDEZ return; 72244993d38SGabriel FERNANDEZ 72344993d38SGabriel FERNANDEZ parents = clkgen_mux_get_parents(np, &num_parents); 72444993d38SGabriel FERNANDEZ if (IS_ERR(parents)) 72586665d28SStephen Boyd goto err_parents; 72644993d38SGabriel FERNANDEZ 72744993d38SGabriel FERNANDEZ clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL); 72844993d38SGabriel FERNANDEZ if (!clk_data) 72986665d28SStephen Boyd goto err_alloc; 73044993d38SGabriel FERNANDEZ 73144993d38SGabriel FERNANDEZ clk_data->clk_num = VCC_MAX_CHANNELS; 73286665d28SStephen Boyd clk_data->clks = kcalloc(clk_data->clk_num, sizeof(struct clk *), 73344993d38SGabriel FERNANDEZ GFP_KERNEL); 73444993d38SGabriel FERNANDEZ 73544993d38SGabriel FERNANDEZ if (!clk_data->clks) 73686665d28SStephen Boyd goto err_alloc_clks; 73744993d38SGabriel FERNANDEZ 73844993d38SGabriel FERNANDEZ for (i = 0; i < clk_data->clk_num; i++) { 73944993d38SGabriel FERNANDEZ struct clk *clk; 74044993d38SGabriel FERNANDEZ const char *clk_name; 74144993d38SGabriel FERNANDEZ struct clk_gate *gate; 74244993d38SGabriel FERNANDEZ struct clk_divider *div; 74344993d38SGabriel FERNANDEZ struct clk_mux *mux; 74444993d38SGabriel FERNANDEZ 74544993d38SGabriel FERNANDEZ if (of_property_read_string_index(np, "clock-output-names", 74644993d38SGabriel FERNANDEZ i, &clk_name)) 74744993d38SGabriel FERNANDEZ break; 74844993d38SGabriel FERNANDEZ 74944993d38SGabriel FERNANDEZ /* 75044993d38SGabriel FERNANDEZ * If we read an empty clock name then the output is unused 75144993d38SGabriel FERNANDEZ */ 75244993d38SGabriel FERNANDEZ if (*clk_name == '\0') 75344993d38SGabriel FERNANDEZ continue; 75444993d38SGabriel FERNANDEZ 75586665d28SStephen Boyd gate = kzalloc(sizeof(*gate), GFP_KERNEL); 75644993d38SGabriel FERNANDEZ if (!gate) 75786665d28SStephen Boyd goto err; 75844993d38SGabriel FERNANDEZ 75986665d28SStephen Boyd div = kzalloc(sizeof(*div), GFP_KERNEL); 76044993d38SGabriel FERNANDEZ if (!div) { 76144993d38SGabriel FERNANDEZ kfree(gate); 76286665d28SStephen Boyd goto err; 76344993d38SGabriel FERNANDEZ } 76444993d38SGabriel FERNANDEZ 76586665d28SStephen Boyd mux = kzalloc(sizeof(*mux), GFP_KERNEL); 76644993d38SGabriel FERNANDEZ if (!mux) { 76744993d38SGabriel FERNANDEZ kfree(gate); 76844993d38SGabriel FERNANDEZ kfree(div); 76986665d28SStephen Boyd goto err; 77044993d38SGabriel FERNANDEZ } 77144993d38SGabriel FERNANDEZ 77244993d38SGabriel FERNANDEZ gate->reg = reg + VCC_GATE_OFFSET; 77344993d38SGabriel FERNANDEZ gate->bit_idx = i; 77444993d38SGabriel FERNANDEZ gate->flags = CLK_GATE_SET_TO_DISABLE; 77544993d38SGabriel FERNANDEZ gate->lock = data->lock; 77644993d38SGabriel FERNANDEZ 77744993d38SGabriel FERNANDEZ div->reg = reg + VCC_DIV_OFFSET; 77844993d38SGabriel FERNANDEZ div->shift = 2 * i; 77944993d38SGabriel FERNANDEZ div->width = 2; 780eee40bb4SGabriel FERNANDEZ div->flags = CLK_DIVIDER_POWER_OF_TWO | 781eee40bb4SGabriel FERNANDEZ CLK_DIVIDER_ROUND_CLOSEST; 78244993d38SGabriel FERNANDEZ 78344993d38SGabriel FERNANDEZ mux->reg = reg + VCC_MUX_OFFSET; 78444993d38SGabriel FERNANDEZ mux->shift = 2 * i; 78544993d38SGabriel FERNANDEZ mux->mask = 0x3; 78644993d38SGabriel FERNANDEZ 78744993d38SGabriel FERNANDEZ clk = clk_register_composite(NULL, clk_name, parents, 78844993d38SGabriel FERNANDEZ num_parents, 78944993d38SGabriel FERNANDEZ &mux->hw, &clk_mux_ops, 79044993d38SGabriel FERNANDEZ &div->hw, &clk_divider_ops, 79144993d38SGabriel FERNANDEZ &gate->hw, &clk_gate_ops, 79218fee453SPankaj Dev data->clk_flags | 79318fee453SPankaj Dev CLK_GET_RATE_NOCACHE); 79444993d38SGabriel FERNANDEZ if (IS_ERR(clk)) { 79544993d38SGabriel FERNANDEZ kfree(gate); 79644993d38SGabriel FERNANDEZ kfree(div); 79744993d38SGabriel FERNANDEZ kfree(mux); 79844993d38SGabriel FERNANDEZ goto err; 79944993d38SGabriel FERNANDEZ } 80044993d38SGabriel FERNANDEZ 80144993d38SGabriel FERNANDEZ pr_debug("%s: parent %s rate %u\n", 80244993d38SGabriel FERNANDEZ __clk_get_name(clk), 80344993d38SGabriel FERNANDEZ __clk_get_name(clk_get_parent(clk)), 80444993d38SGabriel FERNANDEZ (unsigned int)clk_get_rate(clk)); 80544993d38SGabriel FERNANDEZ 80644993d38SGabriel FERNANDEZ clk_data->clks[i] = clk; 80744993d38SGabriel FERNANDEZ } 80844993d38SGabriel FERNANDEZ 80944993d38SGabriel FERNANDEZ kfree(parents); 81044993d38SGabriel FERNANDEZ 81144993d38SGabriel FERNANDEZ of_clk_add_provider(np, of_clk_src_onecell_get, clk_data); 81244993d38SGabriel FERNANDEZ return; 81344993d38SGabriel FERNANDEZ 81444993d38SGabriel FERNANDEZ err: 81544993d38SGabriel FERNANDEZ for (i = 0; i < clk_data->clk_num; i++) { 81644993d38SGabriel FERNANDEZ struct clk_composite *composite; 81744993d38SGabriel FERNANDEZ 81844993d38SGabriel FERNANDEZ if (!clk_data->clks[i]) 81944993d38SGabriel FERNANDEZ continue; 82044993d38SGabriel FERNANDEZ 82144993d38SGabriel FERNANDEZ composite = container_of(__clk_get_hw(clk_data->clks[i]), 82244993d38SGabriel FERNANDEZ struct clk_composite, hw); 82344993d38SGabriel FERNANDEZ kfree(container_of(composite->gate_hw, struct clk_gate, hw)); 82444993d38SGabriel FERNANDEZ kfree(container_of(composite->rate_hw, struct clk_divider, hw)); 82544993d38SGabriel FERNANDEZ kfree(container_of(composite->mux_hw, struct clk_mux, hw)); 82644993d38SGabriel FERNANDEZ } 82744993d38SGabriel FERNANDEZ 82844993d38SGabriel FERNANDEZ kfree(clk_data->clks); 82986665d28SStephen Boyd err_alloc_clks: 83044993d38SGabriel FERNANDEZ kfree(clk_data); 83186665d28SStephen Boyd err_alloc: 83244993d38SGabriel FERNANDEZ kfree(parents); 83386665d28SStephen Boyd err_parents: 83486665d28SStephen Boyd iounmap(reg); 83544993d38SGabriel FERNANDEZ } 83644993d38SGabriel FERNANDEZ CLK_OF_DECLARE(clkgen_vcc, "st,clkgen-vcc", st_of_clkgen_vcc_setup); 837