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 DEFINE_SPINLOCK(clkgena_divmux_lock); 2344993d38SGabriel FERNANDEZ static DEFINE_SPINLOCK(clkgenf_lock); 2494885fafSGabriel FERNANDEZ 2594885fafSGabriel FERNANDEZ static const char ** __init clkgen_mux_get_parents(struct device_node *np, 2694885fafSGabriel FERNANDEZ int *num_parents) 2794885fafSGabriel FERNANDEZ { 2894885fafSGabriel FERNANDEZ const char **parents; 29*caeb057cSStephen Boyd unsigned int nparents; 3094885fafSGabriel FERNANDEZ 310a65239cSGeert Uytterhoeven nparents = of_clk_get_parent_count(np); 32*caeb057cSStephen Boyd if (WARN_ON(!nparents)) 3394885fafSGabriel FERNANDEZ return ERR_PTR(-EINVAL); 3494885fafSGabriel FERNANDEZ 3586665d28SStephen Boyd parents = kcalloc(nparents, sizeof(const char *), GFP_KERNEL); 3694885fafSGabriel FERNANDEZ if (!parents) 3794885fafSGabriel FERNANDEZ return ERR_PTR(-ENOMEM); 3894885fafSGabriel FERNANDEZ 390b4e7f08SDinh Nguyen *num_parents = of_clk_parent_fill(np, parents, nparents); 4094885fafSGabriel FERNANDEZ return parents; 4194885fafSGabriel FERNANDEZ } 4294885fafSGabriel FERNANDEZ 4394885fafSGabriel FERNANDEZ /** 4494885fafSGabriel FERNANDEZ * DOC: Clock mux with a programmable divider on each of its three inputs. 4594885fafSGabriel FERNANDEZ * The mux has an input setting which effectively gates its output. 4694885fafSGabriel FERNANDEZ * 4794885fafSGabriel FERNANDEZ * Traits of this clock: 4894885fafSGabriel FERNANDEZ * prepare - clk_(un)prepare only ensures parent is (un)prepared 4994885fafSGabriel FERNANDEZ * enable - clk_enable and clk_disable are functional & control gating 5094885fafSGabriel FERNANDEZ * rate - set rate is supported 5194885fafSGabriel FERNANDEZ * parent - set/get parent 5294885fafSGabriel FERNANDEZ */ 5394885fafSGabriel FERNANDEZ 5494885fafSGabriel FERNANDEZ #define NUM_INPUTS 3 5594885fafSGabriel FERNANDEZ 5694885fafSGabriel FERNANDEZ struct clkgena_divmux { 5794885fafSGabriel FERNANDEZ struct clk_hw hw; 5894885fafSGabriel FERNANDEZ /* Subclassed mux and divider structures */ 5994885fafSGabriel FERNANDEZ struct clk_mux mux; 6094885fafSGabriel FERNANDEZ struct clk_divider div[NUM_INPUTS]; 6194885fafSGabriel FERNANDEZ /* Enable/running feedback register bits for each input */ 6294885fafSGabriel FERNANDEZ void __iomem *feedback_reg[NUM_INPUTS]; 6394885fafSGabriel FERNANDEZ int feedback_bit_idx; 6494885fafSGabriel FERNANDEZ 6594885fafSGabriel FERNANDEZ u8 muxsel; 6694885fafSGabriel FERNANDEZ }; 6794885fafSGabriel FERNANDEZ 6894885fafSGabriel FERNANDEZ #define to_clkgena_divmux(_hw) container_of(_hw, struct clkgena_divmux, hw) 6994885fafSGabriel FERNANDEZ 7094885fafSGabriel FERNANDEZ struct clkgena_divmux_data { 7194885fafSGabriel FERNANDEZ int num_outputs; 7294885fafSGabriel FERNANDEZ int mux_offset; 7394885fafSGabriel FERNANDEZ int mux_offset2; 7494885fafSGabriel FERNANDEZ int mux_start_bit; 7594885fafSGabriel FERNANDEZ int div_offsets[NUM_INPUTS]; 7694885fafSGabriel FERNANDEZ int fb_offsets[NUM_INPUTS]; 7794885fafSGabriel FERNANDEZ int fb_start_bit_idx; 7894885fafSGabriel FERNANDEZ }; 7994885fafSGabriel FERNANDEZ 8094885fafSGabriel FERNANDEZ #define CKGAX_CLKOPSRC_SWITCH_OFF 0x3 8194885fafSGabriel FERNANDEZ 8294885fafSGabriel FERNANDEZ static int clkgena_divmux_is_running(struct clkgena_divmux *mux) 8394885fafSGabriel FERNANDEZ { 8494885fafSGabriel FERNANDEZ u32 regval = readl(mux->feedback_reg[mux->muxsel]); 8594885fafSGabriel FERNANDEZ u32 running = regval & BIT(mux->feedback_bit_idx); 8694885fafSGabriel FERNANDEZ return !!running; 8794885fafSGabriel FERNANDEZ } 8894885fafSGabriel FERNANDEZ 8994885fafSGabriel FERNANDEZ static int clkgena_divmux_enable(struct clk_hw *hw) 9094885fafSGabriel FERNANDEZ { 9194885fafSGabriel FERNANDEZ struct clkgena_divmux *genamux = to_clkgena_divmux(hw); 9294885fafSGabriel FERNANDEZ struct clk_hw *mux_hw = &genamux->mux.hw; 9394885fafSGabriel FERNANDEZ unsigned long timeout; 9494885fafSGabriel FERNANDEZ int ret = 0; 9594885fafSGabriel FERNANDEZ 964e907ef6SJavier Martinez Canillas __clk_hw_set_clk(mux_hw, hw); 9794885fafSGabriel FERNANDEZ 9894885fafSGabriel FERNANDEZ ret = clk_mux_ops.set_parent(mux_hw, genamux->muxsel); 9994885fafSGabriel FERNANDEZ if (ret) 10094885fafSGabriel FERNANDEZ return ret; 10194885fafSGabriel FERNANDEZ 10294885fafSGabriel FERNANDEZ timeout = jiffies + msecs_to_jiffies(10); 10394885fafSGabriel FERNANDEZ 10494885fafSGabriel FERNANDEZ while (!clkgena_divmux_is_running(genamux)) { 10594885fafSGabriel FERNANDEZ if (time_after(jiffies, timeout)) 10694885fafSGabriel FERNANDEZ return -ETIMEDOUT; 10794885fafSGabriel FERNANDEZ cpu_relax(); 10894885fafSGabriel FERNANDEZ } 10994885fafSGabriel FERNANDEZ 11094885fafSGabriel FERNANDEZ return 0; 11194885fafSGabriel FERNANDEZ } 11294885fafSGabriel FERNANDEZ 11394885fafSGabriel FERNANDEZ static void clkgena_divmux_disable(struct clk_hw *hw) 11494885fafSGabriel FERNANDEZ { 11594885fafSGabriel FERNANDEZ struct clkgena_divmux *genamux = to_clkgena_divmux(hw); 11694885fafSGabriel FERNANDEZ struct clk_hw *mux_hw = &genamux->mux.hw; 11794885fafSGabriel FERNANDEZ 1184e907ef6SJavier Martinez Canillas __clk_hw_set_clk(mux_hw, hw); 11994885fafSGabriel FERNANDEZ 12094885fafSGabriel FERNANDEZ clk_mux_ops.set_parent(mux_hw, CKGAX_CLKOPSRC_SWITCH_OFF); 12194885fafSGabriel FERNANDEZ } 12294885fafSGabriel FERNANDEZ 12394885fafSGabriel FERNANDEZ static int clkgena_divmux_is_enabled(struct clk_hw *hw) 12494885fafSGabriel FERNANDEZ { 12594885fafSGabriel FERNANDEZ struct clkgena_divmux *genamux = to_clkgena_divmux(hw); 12694885fafSGabriel FERNANDEZ struct clk_hw *mux_hw = &genamux->mux.hw; 12794885fafSGabriel FERNANDEZ 1284e907ef6SJavier Martinez Canillas __clk_hw_set_clk(mux_hw, hw); 12994885fafSGabriel FERNANDEZ 13094885fafSGabriel FERNANDEZ return (s8)clk_mux_ops.get_parent(mux_hw) > 0; 13194885fafSGabriel FERNANDEZ } 13294885fafSGabriel FERNANDEZ 1338e6dd77cSStephen Boyd static u8 clkgena_divmux_get_parent(struct clk_hw *hw) 13494885fafSGabriel FERNANDEZ { 13594885fafSGabriel FERNANDEZ struct clkgena_divmux *genamux = to_clkgena_divmux(hw); 13694885fafSGabriel FERNANDEZ struct clk_hw *mux_hw = &genamux->mux.hw; 13794885fafSGabriel FERNANDEZ 1384e907ef6SJavier Martinez Canillas __clk_hw_set_clk(mux_hw, hw); 13994885fafSGabriel FERNANDEZ 14094885fafSGabriel FERNANDEZ genamux->muxsel = clk_mux_ops.get_parent(mux_hw); 14194885fafSGabriel FERNANDEZ if ((s8)genamux->muxsel < 0) { 14294885fafSGabriel FERNANDEZ pr_debug("%s: %s: Invalid parent, setting to default.\n", 143836ee0f7SStephen Boyd __func__, clk_hw_get_name(hw)); 14494885fafSGabriel FERNANDEZ genamux->muxsel = 0; 14594885fafSGabriel FERNANDEZ } 14694885fafSGabriel FERNANDEZ 14794885fafSGabriel FERNANDEZ return genamux->muxsel; 14894885fafSGabriel FERNANDEZ } 14994885fafSGabriel FERNANDEZ 15094885fafSGabriel FERNANDEZ static int clkgena_divmux_set_parent(struct clk_hw *hw, u8 index) 15194885fafSGabriel FERNANDEZ { 15294885fafSGabriel FERNANDEZ struct clkgena_divmux *genamux = to_clkgena_divmux(hw); 15394885fafSGabriel FERNANDEZ 15494885fafSGabriel FERNANDEZ if (index >= CKGAX_CLKOPSRC_SWITCH_OFF) 15594885fafSGabriel FERNANDEZ return -EINVAL; 15694885fafSGabriel FERNANDEZ 15794885fafSGabriel FERNANDEZ genamux->muxsel = index; 15894885fafSGabriel FERNANDEZ 15994885fafSGabriel FERNANDEZ /* 16094885fafSGabriel FERNANDEZ * If the mux is already enabled, call enable directly to set the 16194885fafSGabriel FERNANDEZ * new mux position and wait for it to start running again. Otherwise 16294885fafSGabriel FERNANDEZ * do nothing. 16394885fafSGabriel FERNANDEZ */ 16494885fafSGabriel FERNANDEZ if (clkgena_divmux_is_enabled(hw)) 16594885fafSGabriel FERNANDEZ clkgena_divmux_enable(hw); 16694885fafSGabriel FERNANDEZ 16794885fafSGabriel FERNANDEZ return 0; 16894885fafSGabriel FERNANDEZ } 16994885fafSGabriel FERNANDEZ 1708e6dd77cSStephen Boyd static unsigned long clkgena_divmux_recalc_rate(struct clk_hw *hw, 17194885fafSGabriel FERNANDEZ unsigned long parent_rate) 17294885fafSGabriel FERNANDEZ { 17394885fafSGabriel FERNANDEZ struct clkgena_divmux *genamux = to_clkgena_divmux(hw); 17494885fafSGabriel FERNANDEZ struct clk_hw *div_hw = &genamux->div[genamux->muxsel].hw; 17594885fafSGabriel FERNANDEZ 1764e907ef6SJavier Martinez Canillas __clk_hw_set_clk(div_hw, hw); 17794885fafSGabriel FERNANDEZ 17894885fafSGabriel FERNANDEZ return clk_divider_ops.recalc_rate(div_hw, parent_rate); 17994885fafSGabriel FERNANDEZ } 18094885fafSGabriel FERNANDEZ 18194885fafSGabriel FERNANDEZ static int clkgena_divmux_set_rate(struct clk_hw *hw, unsigned long rate, 18294885fafSGabriel FERNANDEZ unsigned long parent_rate) 18394885fafSGabriel FERNANDEZ { 18494885fafSGabriel FERNANDEZ struct clkgena_divmux *genamux = to_clkgena_divmux(hw); 18594885fafSGabriel FERNANDEZ struct clk_hw *div_hw = &genamux->div[genamux->muxsel].hw; 18694885fafSGabriel FERNANDEZ 1874e907ef6SJavier Martinez Canillas __clk_hw_set_clk(div_hw, hw); 18894885fafSGabriel FERNANDEZ 18994885fafSGabriel FERNANDEZ return clk_divider_ops.set_rate(div_hw, rate, parent_rate); 19094885fafSGabriel FERNANDEZ } 19194885fafSGabriel FERNANDEZ 19294885fafSGabriel FERNANDEZ static long clkgena_divmux_round_rate(struct clk_hw *hw, unsigned long rate, 19394885fafSGabriel FERNANDEZ unsigned long *prate) 19494885fafSGabriel FERNANDEZ { 19594885fafSGabriel FERNANDEZ struct clkgena_divmux *genamux = to_clkgena_divmux(hw); 19694885fafSGabriel FERNANDEZ struct clk_hw *div_hw = &genamux->div[genamux->muxsel].hw; 19794885fafSGabriel FERNANDEZ 1984e907ef6SJavier Martinez Canillas __clk_hw_set_clk(div_hw, hw); 19994885fafSGabriel FERNANDEZ 20094885fafSGabriel FERNANDEZ return clk_divider_ops.round_rate(div_hw, rate, prate); 20194885fafSGabriel FERNANDEZ } 20294885fafSGabriel FERNANDEZ 20394885fafSGabriel FERNANDEZ static const struct clk_ops clkgena_divmux_ops = { 20494885fafSGabriel FERNANDEZ .enable = clkgena_divmux_enable, 20594885fafSGabriel FERNANDEZ .disable = clkgena_divmux_disable, 20694885fafSGabriel FERNANDEZ .is_enabled = clkgena_divmux_is_enabled, 20794885fafSGabriel FERNANDEZ .get_parent = clkgena_divmux_get_parent, 20894885fafSGabriel FERNANDEZ .set_parent = clkgena_divmux_set_parent, 20994885fafSGabriel FERNANDEZ .round_rate = clkgena_divmux_round_rate, 21094885fafSGabriel FERNANDEZ .recalc_rate = clkgena_divmux_recalc_rate, 21194885fafSGabriel FERNANDEZ .set_rate = clkgena_divmux_set_rate, 21294885fafSGabriel FERNANDEZ }; 21394885fafSGabriel FERNANDEZ 21494885fafSGabriel FERNANDEZ /** 21594885fafSGabriel FERNANDEZ * clk_register_genamux - register a genamux clock with the clock framework 21694885fafSGabriel FERNANDEZ */ 21786665d28SStephen Boyd static struct clk * __init clk_register_genamux(const char *name, 21894885fafSGabriel FERNANDEZ const char **parent_names, u8 num_parents, 21994885fafSGabriel FERNANDEZ void __iomem *reg, 22094885fafSGabriel FERNANDEZ const struct clkgena_divmux_data *muxdata, 22194885fafSGabriel FERNANDEZ u32 idx) 22294885fafSGabriel FERNANDEZ { 22394885fafSGabriel FERNANDEZ /* 22494885fafSGabriel FERNANDEZ * Fixed constants across all ClockgenA variants 22594885fafSGabriel FERNANDEZ */ 22694885fafSGabriel FERNANDEZ const int mux_width = 2; 22794885fafSGabriel FERNANDEZ const int divider_width = 5; 22894885fafSGabriel FERNANDEZ struct clkgena_divmux *genamux; 22994885fafSGabriel FERNANDEZ struct clk *clk; 23094885fafSGabriel FERNANDEZ struct clk_init_data init; 23194885fafSGabriel FERNANDEZ int i; 23294885fafSGabriel FERNANDEZ 23394885fafSGabriel FERNANDEZ genamux = kzalloc(sizeof(*genamux), GFP_KERNEL); 23494885fafSGabriel FERNANDEZ if (!genamux) 23594885fafSGabriel FERNANDEZ return ERR_PTR(-ENOMEM); 23694885fafSGabriel FERNANDEZ 23794885fafSGabriel FERNANDEZ init.name = name; 23894885fafSGabriel FERNANDEZ init.ops = &clkgena_divmux_ops; 23918fee453SPankaj Dev init.flags = CLK_IS_BASIC | CLK_GET_RATE_NOCACHE; 24094885fafSGabriel FERNANDEZ init.parent_names = parent_names; 24194885fafSGabriel FERNANDEZ init.num_parents = num_parents; 24294885fafSGabriel FERNANDEZ 24394885fafSGabriel FERNANDEZ genamux->mux.lock = &clkgena_divmux_lock; 24494885fafSGabriel FERNANDEZ genamux->mux.mask = BIT(mux_width) - 1; 24594885fafSGabriel FERNANDEZ genamux->mux.shift = muxdata->mux_start_bit + (idx * mux_width); 24694885fafSGabriel FERNANDEZ if (genamux->mux.shift > 31) { 24794885fafSGabriel FERNANDEZ /* 24894885fafSGabriel FERNANDEZ * We have spilled into the second mux register so 24994885fafSGabriel FERNANDEZ * adjust the register address and the bit shift accordingly 25094885fafSGabriel FERNANDEZ */ 25194885fafSGabriel FERNANDEZ genamux->mux.reg = reg + muxdata->mux_offset2; 25294885fafSGabriel FERNANDEZ genamux->mux.shift -= 32; 25394885fafSGabriel FERNANDEZ } else { 25494885fafSGabriel FERNANDEZ genamux->mux.reg = reg + muxdata->mux_offset; 25594885fafSGabriel FERNANDEZ } 25694885fafSGabriel FERNANDEZ 25794885fafSGabriel FERNANDEZ for (i = 0; i < NUM_INPUTS; i++) { 25894885fafSGabriel FERNANDEZ /* 25994885fafSGabriel FERNANDEZ * Divider config for each input 26094885fafSGabriel FERNANDEZ */ 26194885fafSGabriel FERNANDEZ void __iomem *divbase = reg + muxdata->div_offsets[i]; 26294885fafSGabriel FERNANDEZ genamux->div[i].width = divider_width; 26394885fafSGabriel FERNANDEZ genamux->div[i].reg = divbase + (idx * sizeof(u32)); 26494885fafSGabriel FERNANDEZ 26594885fafSGabriel FERNANDEZ /* 26694885fafSGabriel FERNANDEZ * Mux enabled/running feedback register for each input. 26794885fafSGabriel FERNANDEZ */ 26894885fafSGabriel FERNANDEZ genamux->feedback_reg[i] = reg + muxdata->fb_offsets[i]; 26994885fafSGabriel FERNANDEZ } 27094885fafSGabriel FERNANDEZ 27194885fafSGabriel FERNANDEZ genamux->feedback_bit_idx = muxdata->fb_start_bit_idx + idx; 27294885fafSGabriel FERNANDEZ genamux->hw.init = &init; 27394885fafSGabriel FERNANDEZ 27494885fafSGabriel FERNANDEZ clk = clk_register(NULL, &genamux->hw); 27594885fafSGabriel FERNANDEZ if (IS_ERR(clk)) { 27694885fafSGabriel FERNANDEZ kfree(genamux); 27794885fafSGabriel FERNANDEZ goto err; 27894885fafSGabriel FERNANDEZ } 27994885fafSGabriel FERNANDEZ 28094885fafSGabriel FERNANDEZ pr_debug("%s: parent %s rate %lu\n", 28194885fafSGabriel FERNANDEZ __clk_get_name(clk), 28294885fafSGabriel FERNANDEZ __clk_get_name(clk_get_parent(clk)), 28394885fafSGabriel FERNANDEZ clk_get_rate(clk)); 28494885fafSGabriel FERNANDEZ err: 28594885fafSGabriel FERNANDEZ return clk; 28694885fafSGabriel FERNANDEZ } 28794885fafSGabriel FERNANDEZ 28894885fafSGabriel FERNANDEZ static struct clkgena_divmux_data st_divmux_c65hs = { 28994885fafSGabriel FERNANDEZ .num_outputs = 4, 29094885fafSGabriel FERNANDEZ .mux_offset = 0x14, 29194885fafSGabriel FERNANDEZ .mux_start_bit = 0, 29294885fafSGabriel FERNANDEZ .div_offsets = { 0x800, 0x900, 0xb00 }, 29394885fafSGabriel FERNANDEZ .fb_offsets = { 0x18, 0x1c, 0x20 }, 29494885fafSGabriel FERNANDEZ .fb_start_bit_idx = 0, 29594885fafSGabriel FERNANDEZ }; 29694885fafSGabriel FERNANDEZ 29794885fafSGabriel FERNANDEZ static struct clkgena_divmux_data st_divmux_c65ls = { 29894885fafSGabriel FERNANDEZ .num_outputs = 14, 29994885fafSGabriel FERNANDEZ .mux_offset = 0x14, 30094885fafSGabriel FERNANDEZ .mux_offset2 = 0x24, 30194885fafSGabriel FERNANDEZ .mux_start_bit = 8, 30294885fafSGabriel FERNANDEZ .div_offsets = { 0x810, 0xa10, 0xb10 }, 30394885fafSGabriel FERNANDEZ .fb_offsets = { 0x18, 0x1c, 0x20 }, 30494885fafSGabriel FERNANDEZ .fb_start_bit_idx = 4, 30594885fafSGabriel FERNANDEZ }; 30694885fafSGabriel FERNANDEZ 30794885fafSGabriel FERNANDEZ static struct clkgena_divmux_data st_divmux_c32odf0 = { 30894885fafSGabriel FERNANDEZ .num_outputs = 8, 30994885fafSGabriel FERNANDEZ .mux_offset = 0x1c, 31094885fafSGabriel FERNANDEZ .mux_start_bit = 0, 31194885fafSGabriel FERNANDEZ .div_offsets = { 0x800, 0x900, 0xa60 }, 31294885fafSGabriel FERNANDEZ .fb_offsets = { 0x2c, 0x24, 0x28 }, 31394885fafSGabriel FERNANDEZ .fb_start_bit_idx = 0, 31494885fafSGabriel FERNANDEZ }; 31594885fafSGabriel FERNANDEZ 31694885fafSGabriel FERNANDEZ static struct clkgena_divmux_data st_divmux_c32odf1 = { 31794885fafSGabriel FERNANDEZ .num_outputs = 8, 31894885fafSGabriel FERNANDEZ .mux_offset = 0x1c, 31994885fafSGabriel FERNANDEZ .mux_start_bit = 16, 32094885fafSGabriel FERNANDEZ .div_offsets = { 0x820, 0x980, 0xa80 }, 32194885fafSGabriel FERNANDEZ .fb_offsets = { 0x2c, 0x24, 0x28 }, 32294885fafSGabriel FERNANDEZ .fb_start_bit_idx = 8, 32394885fafSGabriel FERNANDEZ }; 32494885fafSGabriel FERNANDEZ 32594885fafSGabriel FERNANDEZ static struct clkgena_divmux_data st_divmux_c32odf2 = { 32694885fafSGabriel FERNANDEZ .num_outputs = 8, 32794885fafSGabriel FERNANDEZ .mux_offset = 0x20, 32894885fafSGabriel FERNANDEZ .mux_start_bit = 0, 32994885fafSGabriel FERNANDEZ .div_offsets = { 0x840, 0xa20, 0xb10 }, 33094885fafSGabriel FERNANDEZ .fb_offsets = { 0x2c, 0x24, 0x28 }, 33194885fafSGabriel FERNANDEZ .fb_start_bit_idx = 16, 33294885fafSGabriel FERNANDEZ }; 33394885fafSGabriel FERNANDEZ 33494885fafSGabriel FERNANDEZ static struct clkgena_divmux_data st_divmux_c32odf3 = { 33594885fafSGabriel FERNANDEZ .num_outputs = 8, 33694885fafSGabriel FERNANDEZ .mux_offset = 0x20, 33794885fafSGabriel FERNANDEZ .mux_start_bit = 16, 33894885fafSGabriel FERNANDEZ .div_offsets = { 0x860, 0xa40, 0xb30 }, 33994885fafSGabriel FERNANDEZ .fb_offsets = { 0x2c, 0x24, 0x28 }, 34094885fafSGabriel FERNANDEZ .fb_start_bit_idx = 24, 34194885fafSGabriel FERNANDEZ }; 34294885fafSGabriel FERNANDEZ 343f375573cSFabian Frederick static const struct of_device_id clkgena_divmux_of_match[] = { 34494885fafSGabriel FERNANDEZ { 34594885fafSGabriel FERNANDEZ .compatible = "st,clkgena-divmux-c65-hs", 34694885fafSGabriel FERNANDEZ .data = &st_divmux_c65hs, 34794885fafSGabriel FERNANDEZ }, 34894885fafSGabriel FERNANDEZ { 34994885fafSGabriel FERNANDEZ .compatible = "st,clkgena-divmux-c65-ls", 35094885fafSGabriel FERNANDEZ .data = &st_divmux_c65ls, 35194885fafSGabriel FERNANDEZ }, 35294885fafSGabriel FERNANDEZ { 35394885fafSGabriel FERNANDEZ .compatible = "st,clkgena-divmux-c32-odf0", 35494885fafSGabriel FERNANDEZ .data = &st_divmux_c32odf0, 35594885fafSGabriel FERNANDEZ }, 35694885fafSGabriel FERNANDEZ { 35794885fafSGabriel FERNANDEZ .compatible = "st,clkgena-divmux-c32-odf1", 35894885fafSGabriel FERNANDEZ .data = &st_divmux_c32odf1, 35994885fafSGabriel FERNANDEZ }, 36094885fafSGabriel FERNANDEZ { 36194885fafSGabriel FERNANDEZ .compatible = "st,clkgena-divmux-c32-odf2", 36294885fafSGabriel FERNANDEZ .data = &st_divmux_c32odf2, 36394885fafSGabriel FERNANDEZ }, 36494885fafSGabriel FERNANDEZ { 36594885fafSGabriel FERNANDEZ .compatible = "st,clkgena-divmux-c32-odf3", 36694885fafSGabriel FERNANDEZ .data = &st_divmux_c32odf3, 36794885fafSGabriel FERNANDEZ }, 36894885fafSGabriel FERNANDEZ {} 36994885fafSGabriel FERNANDEZ }; 37094885fafSGabriel FERNANDEZ 37186665d28SStephen Boyd static void __iomem * __init clkgen_get_register_base(struct device_node *np) 37294885fafSGabriel FERNANDEZ { 37394885fafSGabriel FERNANDEZ struct device_node *pnode; 37486665d28SStephen Boyd void __iomem *reg; 37594885fafSGabriel FERNANDEZ 37694885fafSGabriel FERNANDEZ pnode = of_get_parent(np); 37794885fafSGabriel FERNANDEZ if (!pnode) 37894885fafSGabriel FERNANDEZ return NULL; 37994885fafSGabriel FERNANDEZ 38094885fafSGabriel FERNANDEZ reg = of_iomap(pnode, 0); 38194885fafSGabriel FERNANDEZ 38294885fafSGabriel FERNANDEZ of_node_put(pnode); 38394885fafSGabriel FERNANDEZ return reg; 38494885fafSGabriel FERNANDEZ } 38594885fafSGabriel FERNANDEZ 3868e6dd77cSStephen Boyd static void __init st_of_clkgena_divmux_setup(struct device_node *np) 38794885fafSGabriel FERNANDEZ { 38894885fafSGabriel FERNANDEZ const struct of_device_id *match; 38994885fafSGabriel FERNANDEZ const struct clkgena_divmux_data *data; 39094885fafSGabriel FERNANDEZ struct clk_onecell_data *clk_data; 39194885fafSGabriel FERNANDEZ void __iomem *reg; 39294885fafSGabriel FERNANDEZ const char **parents; 39394885fafSGabriel FERNANDEZ int num_parents = 0, i; 39494885fafSGabriel FERNANDEZ 39594885fafSGabriel FERNANDEZ match = of_match_node(clkgena_divmux_of_match, np); 39694885fafSGabriel FERNANDEZ if (WARN_ON(!match)) 39794885fafSGabriel FERNANDEZ return; 39894885fafSGabriel FERNANDEZ 39986665d28SStephen Boyd data = match->data; 40094885fafSGabriel FERNANDEZ 40194885fafSGabriel FERNANDEZ reg = clkgen_get_register_base(np); 40294885fafSGabriel FERNANDEZ if (!reg) 40394885fafSGabriel FERNANDEZ return; 40494885fafSGabriel FERNANDEZ 40594885fafSGabriel FERNANDEZ parents = clkgen_mux_get_parents(np, &num_parents); 40694885fafSGabriel FERNANDEZ if (IS_ERR(parents)) 40786665d28SStephen Boyd goto err_parents; 40894885fafSGabriel FERNANDEZ 40994885fafSGabriel FERNANDEZ clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL); 41094885fafSGabriel FERNANDEZ if (!clk_data) 41186665d28SStephen Boyd goto err_alloc; 41294885fafSGabriel FERNANDEZ 41394885fafSGabriel FERNANDEZ clk_data->clk_num = data->num_outputs; 41486665d28SStephen Boyd clk_data->clks = kcalloc(clk_data->clk_num, sizeof(struct clk *), 41594885fafSGabriel FERNANDEZ GFP_KERNEL); 41694885fafSGabriel FERNANDEZ 41794885fafSGabriel FERNANDEZ if (!clk_data->clks) 41886665d28SStephen Boyd goto err_alloc_clks; 41994885fafSGabriel FERNANDEZ 42094885fafSGabriel FERNANDEZ for (i = 0; i < clk_data->clk_num; i++) { 42194885fafSGabriel FERNANDEZ struct clk *clk; 42294885fafSGabriel FERNANDEZ const char *clk_name; 42394885fafSGabriel FERNANDEZ 42494885fafSGabriel FERNANDEZ if (of_property_read_string_index(np, "clock-output-names", 42594885fafSGabriel FERNANDEZ i, &clk_name)) 42694885fafSGabriel FERNANDEZ break; 42794885fafSGabriel FERNANDEZ 42894885fafSGabriel FERNANDEZ /* 42994885fafSGabriel FERNANDEZ * If we read an empty clock name then the output is unused 43094885fafSGabriel FERNANDEZ */ 43194885fafSGabriel FERNANDEZ if (*clk_name == '\0') 43294885fafSGabriel FERNANDEZ continue; 43394885fafSGabriel FERNANDEZ 43494885fafSGabriel FERNANDEZ clk = clk_register_genamux(clk_name, parents, num_parents, 43594885fafSGabriel FERNANDEZ reg, data, i); 43694885fafSGabriel FERNANDEZ 43794885fafSGabriel FERNANDEZ if (IS_ERR(clk)) 43894885fafSGabriel FERNANDEZ goto err; 43994885fafSGabriel FERNANDEZ 44094885fafSGabriel FERNANDEZ clk_data->clks[i] = clk; 44194885fafSGabriel FERNANDEZ } 44294885fafSGabriel FERNANDEZ 44394885fafSGabriel FERNANDEZ kfree(parents); 44494885fafSGabriel FERNANDEZ 44594885fafSGabriel FERNANDEZ of_clk_add_provider(np, of_clk_src_onecell_get, clk_data); 44694885fafSGabriel FERNANDEZ return; 44794885fafSGabriel FERNANDEZ err: 44894885fafSGabriel FERNANDEZ kfree(clk_data->clks); 44986665d28SStephen Boyd err_alloc_clks: 45094885fafSGabriel FERNANDEZ kfree(clk_data); 45186665d28SStephen Boyd err_alloc: 45294885fafSGabriel FERNANDEZ kfree(parents); 45386665d28SStephen Boyd err_parents: 45486665d28SStephen Boyd iounmap(reg); 45594885fafSGabriel FERNANDEZ } 45694885fafSGabriel FERNANDEZ CLK_OF_DECLARE(clkgenadivmux, "st,clkgena-divmux", st_of_clkgena_divmux_setup); 45794885fafSGabriel FERNANDEZ 45894885fafSGabriel FERNANDEZ struct clkgena_prediv_data { 45994885fafSGabriel FERNANDEZ u32 offset; 46094885fafSGabriel FERNANDEZ u8 shift; 46194885fafSGabriel FERNANDEZ struct clk_div_table *table; 46294885fafSGabriel FERNANDEZ }; 46394885fafSGabriel FERNANDEZ 46494885fafSGabriel FERNANDEZ static struct clk_div_table prediv_table16[] = { 46594885fafSGabriel FERNANDEZ { .val = 0, .div = 1 }, 46694885fafSGabriel FERNANDEZ { .val = 1, .div = 16 }, 46794885fafSGabriel FERNANDEZ { .div = 0 }, 46894885fafSGabriel FERNANDEZ }; 46994885fafSGabriel FERNANDEZ 47094885fafSGabriel FERNANDEZ static struct clkgena_prediv_data prediv_c65_data = { 47194885fafSGabriel FERNANDEZ .offset = 0x4c, 47294885fafSGabriel FERNANDEZ .shift = 31, 47394885fafSGabriel FERNANDEZ .table = prediv_table16, 47494885fafSGabriel FERNANDEZ }; 47594885fafSGabriel FERNANDEZ 47694885fafSGabriel FERNANDEZ static struct clkgena_prediv_data prediv_c32_data = { 47794885fafSGabriel FERNANDEZ .offset = 0x50, 47894885fafSGabriel FERNANDEZ .shift = 1, 47994885fafSGabriel FERNANDEZ .table = prediv_table16, 48094885fafSGabriel FERNANDEZ }; 48194885fafSGabriel FERNANDEZ 482f375573cSFabian Frederick static const struct of_device_id clkgena_prediv_of_match[] = { 48394885fafSGabriel FERNANDEZ { .compatible = "st,clkgena-prediv-c65", .data = &prediv_c65_data }, 48494885fafSGabriel FERNANDEZ { .compatible = "st,clkgena-prediv-c32", .data = &prediv_c32_data }, 48594885fafSGabriel FERNANDEZ {} 48694885fafSGabriel FERNANDEZ }; 48794885fafSGabriel FERNANDEZ 4888e6dd77cSStephen Boyd static void __init st_of_clkgena_prediv_setup(struct device_node *np) 48994885fafSGabriel FERNANDEZ { 49094885fafSGabriel FERNANDEZ const struct of_device_id *match; 49194885fafSGabriel FERNANDEZ void __iomem *reg; 49294885fafSGabriel FERNANDEZ const char *parent_name, *clk_name; 49394885fafSGabriel FERNANDEZ struct clk *clk; 49486665d28SStephen Boyd const struct clkgena_prediv_data *data; 49594885fafSGabriel FERNANDEZ 49694885fafSGabriel FERNANDEZ match = of_match_node(clkgena_prediv_of_match, np); 49794885fafSGabriel FERNANDEZ if (!match) { 49894885fafSGabriel FERNANDEZ pr_err("%s: No matching data\n", __func__); 49994885fafSGabriel FERNANDEZ return; 50094885fafSGabriel FERNANDEZ } 50194885fafSGabriel FERNANDEZ 50286665d28SStephen Boyd data = match->data; 50394885fafSGabriel FERNANDEZ 50494885fafSGabriel FERNANDEZ reg = clkgen_get_register_base(np); 50594885fafSGabriel FERNANDEZ if (!reg) 50694885fafSGabriel FERNANDEZ return; 50794885fafSGabriel FERNANDEZ 50894885fafSGabriel FERNANDEZ parent_name = of_clk_get_parent_name(np, 0); 50994885fafSGabriel FERNANDEZ if (!parent_name) 51086665d28SStephen Boyd goto err; 51194885fafSGabriel FERNANDEZ 51294885fafSGabriel FERNANDEZ if (of_property_read_string_index(np, "clock-output-names", 51394885fafSGabriel FERNANDEZ 0, &clk_name)) 51486665d28SStephen Boyd goto err; 51594885fafSGabriel FERNANDEZ 51618fee453SPankaj Dev clk = clk_register_divider_table(NULL, clk_name, parent_name, 51718fee453SPankaj Dev CLK_GET_RATE_NOCACHE, 51894885fafSGabriel FERNANDEZ reg + data->offset, data->shift, 1, 51994885fafSGabriel FERNANDEZ 0, data->table, NULL); 52094885fafSGabriel FERNANDEZ if (IS_ERR(clk)) 52186665d28SStephen Boyd goto err; 52294885fafSGabriel FERNANDEZ 52394885fafSGabriel FERNANDEZ of_clk_add_provider(np, of_clk_src_simple_get, clk); 52494885fafSGabriel FERNANDEZ pr_debug("%s: parent %s rate %u\n", 52594885fafSGabriel FERNANDEZ __clk_get_name(clk), 52694885fafSGabriel FERNANDEZ __clk_get_name(clk_get_parent(clk)), 52794885fafSGabriel FERNANDEZ (unsigned int)clk_get_rate(clk)); 52894885fafSGabriel FERNANDEZ 52994885fafSGabriel FERNANDEZ return; 53086665d28SStephen Boyd err: 53186665d28SStephen Boyd iounmap(reg); 53294885fafSGabriel FERNANDEZ } 53394885fafSGabriel FERNANDEZ CLK_OF_DECLARE(clkgenaprediv, "st,clkgena-prediv", st_of_clkgena_prediv_setup); 53444993d38SGabriel FERNANDEZ 53544993d38SGabriel FERNANDEZ struct clkgen_mux_data { 53644993d38SGabriel FERNANDEZ u32 offset; 53744993d38SGabriel FERNANDEZ u8 shift; 53844993d38SGabriel FERNANDEZ u8 width; 53944993d38SGabriel FERNANDEZ spinlock_t *lock; 54044993d38SGabriel FERNANDEZ unsigned long clk_flags; 54144993d38SGabriel FERNANDEZ u8 mux_flags; 54244993d38SGabriel FERNANDEZ }; 54344993d38SGabriel FERNANDEZ 54444993d38SGabriel FERNANDEZ static struct clkgen_mux_data clkgen_mux_c_vcc_hd_416 = { 54544993d38SGabriel FERNANDEZ .offset = 0, 54644993d38SGabriel FERNANDEZ .shift = 0, 54744993d38SGabriel FERNANDEZ .width = 1, 54844993d38SGabriel FERNANDEZ }; 54944993d38SGabriel FERNANDEZ 55044993d38SGabriel FERNANDEZ static struct clkgen_mux_data clkgen_mux_f_vcc_fvdp_416 = { 55144993d38SGabriel FERNANDEZ .offset = 0, 55244993d38SGabriel FERNANDEZ .shift = 0, 55344993d38SGabriel FERNANDEZ .width = 1, 55444993d38SGabriel FERNANDEZ }; 55544993d38SGabriel FERNANDEZ 55644993d38SGabriel FERNANDEZ static struct clkgen_mux_data clkgen_mux_f_vcc_hva_416 = { 55744993d38SGabriel FERNANDEZ .offset = 0, 55844993d38SGabriel FERNANDEZ .shift = 0, 55944993d38SGabriel FERNANDEZ .width = 1, 56044993d38SGabriel FERNANDEZ }; 56144993d38SGabriel FERNANDEZ 56244993d38SGabriel FERNANDEZ static struct clkgen_mux_data clkgen_mux_f_vcc_hd_416 = { 56344993d38SGabriel FERNANDEZ .offset = 0, 56444993d38SGabriel FERNANDEZ .shift = 16, 56544993d38SGabriel FERNANDEZ .width = 1, 56644993d38SGabriel FERNANDEZ .lock = &clkgenf_lock, 56744993d38SGabriel FERNANDEZ }; 56844993d38SGabriel FERNANDEZ 56944993d38SGabriel FERNANDEZ static struct clkgen_mux_data clkgen_mux_c_vcc_sd_416 = { 57044993d38SGabriel FERNANDEZ .offset = 0, 57144993d38SGabriel FERNANDEZ .shift = 17, 57244993d38SGabriel FERNANDEZ .width = 1, 57344993d38SGabriel FERNANDEZ .lock = &clkgenf_lock, 57444993d38SGabriel FERNANDEZ }; 57544993d38SGabriel FERNANDEZ 576ab35dc13SGabriel FERNANDEZ static struct clkgen_mux_data stih415_a9_mux_data = { 577ab35dc13SGabriel FERNANDEZ .offset = 0, 578ab35dc13SGabriel FERNANDEZ .shift = 1, 579ab35dc13SGabriel FERNANDEZ .width = 2, 58046a57afdSGabriel Fernandez .lock = &clkgen_a9_lock, 581ab35dc13SGabriel FERNANDEZ }; 582ab35dc13SGabriel FERNANDEZ static struct clkgen_mux_data stih416_a9_mux_data = { 583ab35dc13SGabriel FERNANDEZ .offset = 0, 584ab35dc13SGabriel FERNANDEZ .shift = 0, 585ab35dc13SGabriel FERNANDEZ .width = 2, 586ab35dc13SGabriel FERNANDEZ }; 58713e6f2daSGabriel FERNANDEZ static struct clkgen_mux_data stih407_a9_mux_data = { 58813e6f2daSGabriel FERNANDEZ .offset = 0x1a4, 5893be6d8ceSGabriel Fernandez .shift = 0, 59013e6f2daSGabriel FERNANDEZ .width = 2, 59146a57afdSGabriel Fernandez .lock = &clkgen_a9_lock, 59213e6f2daSGabriel FERNANDEZ }; 593ab35dc13SGabriel FERNANDEZ 594f375573cSFabian Frederick static const struct of_device_id mux_of_match[] = { 59544993d38SGabriel FERNANDEZ { 59644993d38SGabriel FERNANDEZ .compatible = "st,stih416-clkgenc-vcc-hd", 59744993d38SGabriel FERNANDEZ .data = &clkgen_mux_c_vcc_hd_416, 59844993d38SGabriel FERNANDEZ }, 59944993d38SGabriel FERNANDEZ { 60044993d38SGabriel FERNANDEZ .compatible = "st,stih416-clkgenf-vcc-fvdp", 60144993d38SGabriel FERNANDEZ .data = &clkgen_mux_f_vcc_fvdp_416, 60244993d38SGabriel FERNANDEZ }, 60344993d38SGabriel FERNANDEZ { 60444993d38SGabriel FERNANDEZ .compatible = "st,stih416-clkgenf-vcc-hva", 60544993d38SGabriel FERNANDEZ .data = &clkgen_mux_f_vcc_hva_416, 60644993d38SGabriel FERNANDEZ }, 60744993d38SGabriel FERNANDEZ { 60844993d38SGabriel FERNANDEZ .compatible = "st,stih416-clkgenf-vcc-hd", 60944993d38SGabriel FERNANDEZ .data = &clkgen_mux_f_vcc_hd_416, 61044993d38SGabriel FERNANDEZ }, 61144993d38SGabriel FERNANDEZ { 61244993d38SGabriel FERNANDEZ .compatible = "st,stih416-clkgenf-vcc-sd", 61344993d38SGabriel FERNANDEZ .data = &clkgen_mux_c_vcc_sd_416, 61444993d38SGabriel FERNANDEZ }, 615ab35dc13SGabriel FERNANDEZ { 616ab35dc13SGabriel FERNANDEZ .compatible = "st,stih415-clkgen-a9-mux", 617ab35dc13SGabriel FERNANDEZ .data = &stih415_a9_mux_data, 618ab35dc13SGabriel FERNANDEZ }, 619ab35dc13SGabriel FERNANDEZ { 620ab35dc13SGabriel FERNANDEZ .compatible = "st,stih416-clkgen-a9-mux", 621ab35dc13SGabriel FERNANDEZ .data = &stih416_a9_mux_data, 622ab35dc13SGabriel FERNANDEZ }, 62313e6f2daSGabriel FERNANDEZ { 62413e6f2daSGabriel FERNANDEZ .compatible = "st,stih407-clkgen-a9-mux", 62513e6f2daSGabriel FERNANDEZ .data = &stih407_a9_mux_data, 62613e6f2daSGabriel FERNANDEZ }, 62744993d38SGabriel FERNANDEZ {} 62844993d38SGabriel FERNANDEZ }; 62944993d38SGabriel FERNANDEZ 6308e6dd77cSStephen Boyd static void __init st_of_clkgen_mux_setup(struct device_node *np) 63144993d38SGabriel FERNANDEZ { 63244993d38SGabriel FERNANDEZ const struct of_device_id *match; 63344993d38SGabriel FERNANDEZ struct clk *clk; 63444993d38SGabriel FERNANDEZ void __iomem *reg; 63544993d38SGabriel FERNANDEZ const char **parents; 63644993d38SGabriel FERNANDEZ int num_parents; 63786665d28SStephen Boyd const struct clkgen_mux_data *data; 63844993d38SGabriel FERNANDEZ 63944993d38SGabriel FERNANDEZ match = of_match_node(mux_of_match, np); 64044993d38SGabriel FERNANDEZ if (!match) { 64144993d38SGabriel FERNANDEZ pr_err("%s: No matching data\n", __func__); 64244993d38SGabriel FERNANDEZ return; 64344993d38SGabriel FERNANDEZ } 64444993d38SGabriel FERNANDEZ 64586665d28SStephen Boyd data = match->data; 64644993d38SGabriel FERNANDEZ 64744993d38SGabriel FERNANDEZ reg = of_iomap(np, 0); 64844993d38SGabriel FERNANDEZ if (!reg) { 64944993d38SGabriel FERNANDEZ pr_err("%s: Failed to get base address\n", __func__); 65044993d38SGabriel FERNANDEZ return; 65144993d38SGabriel FERNANDEZ } 65244993d38SGabriel FERNANDEZ 65344993d38SGabriel FERNANDEZ parents = clkgen_mux_get_parents(np, &num_parents); 65444993d38SGabriel FERNANDEZ if (IS_ERR(parents)) { 65544993d38SGabriel FERNANDEZ pr_err("%s: Failed to get parents (%ld)\n", 65644993d38SGabriel FERNANDEZ __func__, PTR_ERR(parents)); 65786665d28SStephen Boyd goto err_parents; 65844993d38SGabriel FERNANDEZ } 65944993d38SGabriel FERNANDEZ 66044993d38SGabriel FERNANDEZ clk = clk_register_mux(NULL, np->name, parents, num_parents, 66144993d38SGabriel FERNANDEZ data->clk_flags | CLK_SET_RATE_PARENT, 66244993d38SGabriel FERNANDEZ reg + data->offset, 66344993d38SGabriel FERNANDEZ data->shift, data->width, data->mux_flags, 66444993d38SGabriel FERNANDEZ data->lock); 66544993d38SGabriel FERNANDEZ if (IS_ERR(clk)) 66644993d38SGabriel FERNANDEZ goto err; 66744993d38SGabriel FERNANDEZ 66844993d38SGabriel FERNANDEZ pr_debug("%s: parent %s rate %u\n", 66944993d38SGabriel FERNANDEZ __clk_get_name(clk), 67044993d38SGabriel FERNANDEZ __clk_get_name(clk_get_parent(clk)), 67144993d38SGabriel FERNANDEZ (unsigned int)clk_get_rate(clk)); 67244993d38SGabriel FERNANDEZ 67386665d28SStephen Boyd kfree(parents); 67444993d38SGabriel FERNANDEZ of_clk_add_provider(np, of_clk_src_simple_get, clk); 67586665d28SStephen Boyd return; 67644993d38SGabriel FERNANDEZ 67744993d38SGabriel FERNANDEZ err: 67844993d38SGabriel FERNANDEZ kfree(parents); 67986665d28SStephen Boyd err_parents: 68086665d28SStephen Boyd iounmap(reg); 68144993d38SGabriel FERNANDEZ } 68244993d38SGabriel FERNANDEZ CLK_OF_DECLARE(clkgen_mux, "st,clkgen-mux", st_of_clkgen_mux_setup); 68344993d38SGabriel FERNANDEZ 68444993d38SGabriel FERNANDEZ #define VCC_MAX_CHANNELS 16 68544993d38SGabriel FERNANDEZ 68644993d38SGabriel FERNANDEZ #define VCC_GATE_OFFSET 0x0 68744993d38SGabriel FERNANDEZ #define VCC_MUX_OFFSET 0x4 68844993d38SGabriel FERNANDEZ #define VCC_DIV_OFFSET 0x8 68944993d38SGabriel FERNANDEZ 69044993d38SGabriel FERNANDEZ struct clkgen_vcc_data { 69144993d38SGabriel FERNANDEZ spinlock_t *lock; 69244993d38SGabriel FERNANDEZ unsigned long clk_flags; 69344993d38SGabriel FERNANDEZ }; 69444993d38SGabriel FERNANDEZ 69544993d38SGabriel FERNANDEZ static struct clkgen_vcc_data st_clkgenc_vcc_416 = { 69644993d38SGabriel FERNANDEZ .clk_flags = CLK_SET_RATE_PARENT, 69744993d38SGabriel FERNANDEZ }; 69844993d38SGabriel FERNANDEZ 69944993d38SGabriel FERNANDEZ static struct clkgen_vcc_data st_clkgenf_vcc_416 = { 70044993d38SGabriel FERNANDEZ .lock = &clkgenf_lock, 70144993d38SGabriel FERNANDEZ }; 70244993d38SGabriel FERNANDEZ 703f375573cSFabian Frederick static const struct of_device_id vcc_of_match[] = { 70444993d38SGabriel FERNANDEZ { .compatible = "st,stih416-clkgenc", .data = &st_clkgenc_vcc_416 }, 70544993d38SGabriel FERNANDEZ { .compatible = "st,stih416-clkgenf", .data = &st_clkgenf_vcc_416 }, 70644993d38SGabriel FERNANDEZ {} 70744993d38SGabriel FERNANDEZ }; 70844993d38SGabriel FERNANDEZ 7098e6dd77cSStephen Boyd static void __init st_of_clkgen_vcc_setup(struct device_node *np) 71044993d38SGabriel FERNANDEZ { 71144993d38SGabriel FERNANDEZ const struct of_device_id *match; 71244993d38SGabriel FERNANDEZ void __iomem *reg; 71344993d38SGabriel FERNANDEZ const char **parents; 71444993d38SGabriel FERNANDEZ int num_parents, i; 71544993d38SGabriel FERNANDEZ struct clk_onecell_data *clk_data; 71686665d28SStephen Boyd const struct clkgen_vcc_data *data; 71744993d38SGabriel FERNANDEZ 71844993d38SGabriel FERNANDEZ match = of_match_node(vcc_of_match, np); 71944993d38SGabriel FERNANDEZ if (WARN_ON(!match)) 72044993d38SGabriel FERNANDEZ return; 72186665d28SStephen Boyd data = match->data; 72244993d38SGabriel FERNANDEZ 72344993d38SGabriel FERNANDEZ reg = of_iomap(np, 0); 72444993d38SGabriel FERNANDEZ if (!reg) 72544993d38SGabriel FERNANDEZ return; 72644993d38SGabriel FERNANDEZ 72744993d38SGabriel FERNANDEZ parents = clkgen_mux_get_parents(np, &num_parents); 72844993d38SGabriel FERNANDEZ if (IS_ERR(parents)) 72986665d28SStephen Boyd goto err_parents; 73044993d38SGabriel FERNANDEZ 73144993d38SGabriel FERNANDEZ clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL); 73244993d38SGabriel FERNANDEZ if (!clk_data) 73386665d28SStephen Boyd goto err_alloc; 73444993d38SGabriel FERNANDEZ 73544993d38SGabriel FERNANDEZ clk_data->clk_num = VCC_MAX_CHANNELS; 73686665d28SStephen Boyd clk_data->clks = kcalloc(clk_data->clk_num, sizeof(struct clk *), 73744993d38SGabriel FERNANDEZ GFP_KERNEL); 73844993d38SGabriel FERNANDEZ 73944993d38SGabriel FERNANDEZ if (!clk_data->clks) 74086665d28SStephen Boyd goto err_alloc_clks; 74144993d38SGabriel FERNANDEZ 74244993d38SGabriel FERNANDEZ for (i = 0; i < clk_data->clk_num; i++) { 74344993d38SGabriel FERNANDEZ struct clk *clk; 74444993d38SGabriel FERNANDEZ const char *clk_name; 74544993d38SGabriel FERNANDEZ struct clk_gate *gate; 74644993d38SGabriel FERNANDEZ struct clk_divider *div; 74744993d38SGabriel FERNANDEZ struct clk_mux *mux; 74844993d38SGabriel FERNANDEZ 74944993d38SGabriel FERNANDEZ if (of_property_read_string_index(np, "clock-output-names", 75044993d38SGabriel FERNANDEZ i, &clk_name)) 75144993d38SGabriel FERNANDEZ break; 75244993d38SGabriel FERNANDEZ 75344993d38SGabriel FERNANDEZ /* 75444993d38SGabriel FERNANDEZ * If we read an empty clock name then the output is unused 75544993d38SGabriel FERNANDEZ */ 75644993d38SGabriel FERNANDEZ if (*clk_name == '\0') 75744993d38SGabriel FERNANDEZ continue; 75844993d38SGabriel FERNANDEZ 75986665d28SStephen Boyd gate = kzalloc(sizeof(*gate), GFP_KERNEL); 76044993d38SGabriel FERNANDEZ if (!gate) 76186665d28SStephen Boyd goto err; 76244993d38SGabriel FERNANDEZ 76386665d28SStephen Boyd div = kzalloc(sizeof(*div), GFP_KERNEL); 76444993d38SGabriel FERNANDEZ if (!div) { 76544993d38SGabriel FERNANDEZ kfree(gate); 76686665d28SStephen Boyd goto err; 76744993d38SGabriel FERNANDEZ } 76844993d38SGabriel FERNANDEZ 76986665d28SStephen Boyd mux = kzalloc(sizeof(*mux), GFP_KERNEL); 77044993d38SGabriel FERNANDEZ if (!mux) { 77144993d38SGabriel FERNANDEZ kfree(gate); 77244993d38SGabriel FERNANDEZ kfree(div); 77386665d28SStephen Boyd goto err; 77444993d38SGabriel FERNANDEZ } 77544993d38SGabriel FERNANDEZ 77644993d38SGabriel FERNANDEZ gate->reg = reg + VCC_GATE_OFFSET; 77744993d38SGabriel FERNANDEZ gate->bit_idx = i; 77844993d38SGabriel FERNANDEZ gate->flags = CLK_GATE_SET_TO_DISABLE; 77944993d38SGabriel FERNANDEZ gate->lock = data->lock; 78044993d38SGabriel FERNANDEZ 78144993d38SGabriel FERNANDEZ div->reg = reg + VCC_DIV_OFFSET; 78244993d38SGabriel FERNANDEZ div->shift = 2 * i; 78344993d38SGabriel FERNANDEZ div->width = 2; 784eee40bb4SGabriel FERNANDEZ div->flags = CLK_DIVIDER_POWER_OF_TWO | 785eee40bb4SGabriel FERNANDEZ CLK_DIVIDER_ROUND_CLOSEST; 78644993d38SGabriel FERNANDEZ 78744993d38SGabriel FERNANDEZ mux->reg = reg + VCC_MUX_OFFSET; 78844993d38SGabriel FERNANDEZ mux->shift = 2 * i; 78944993d38SGabriel FERNANDEZ mux->mask = 0x3; 79044993d38SGabriel FERNANDEZ 79144993d38SGabriel FERNANDEZ clk = clk_register_composite(NULL, clk_name, parents, 79244993d38SGabriel FERNANDEZ num_parents, 79344993d38SGabriel FERNANDEZ &mux->hw, &clk_mux_ops, 79444993d38SGabriel FERNANDEZ &div->hw, &clk_divider_ops, 79544993d38SGabriel FERNANDEZ &gate->hw, &clk_gate_ops, 79618fee453SPankaj Dev data->clk_flags | 79718fee453SPankaj Dev CLK_GET_RATE_NOCACHE); 79844993d38SGabriel FERNANDEZ if (IS_ERR(clk)) { 79944993d38SGabriel FERNANDEZ kfree(gate); 80044993d38SGabriel FERNANDEZ kfree(div); 80144993d38SGabriel FERNANDEZ kfree(mux); 80244993d38SGabriel FERNANDEZ goto err; 80344993d38SGabriel FERNANDEZ } 80444993d38SGabriel FERNANDEZ 80544993d38SGabriel FERNANDEZ pr_debug("%s: parent %s rate %u\n", 80644993d38SGabriel FERNANDEZ __clk_get_name(clk), 80744993d38SGabriel FERNANDEZ __clk_get_name(clk_get_parent(clk)), 80844993d38SGabriel FERNANDEZ (unsigned int)clk_get_rate(clk)); 80944993d38SGabriel FERNANDEZ 81044993d38SGabriel FERNANDEZ clk_data->clks[i] = clk; 81144993d38SGabriel FERNANDEZ } 81244993d38SGabriel FERNANDEZ 81344993d38SGabriel FERNANDEZ kfree(parents); 81444993d38SGabriel FERNANDEZ 81544993d38SGabriel FERNANDEZ of_clk_add_provider(np, of_clk_src_onecell_get, clk_data); 81644993d38SGabriel FERNANDEZ return; 81744993d38SGabriel FERNANDEZ 81844993d38SGabriel FERNANDEZ err: 81944993d38SGabriel FERNANDEZ for (i = 0; i < clk_data->clk_num; i++) { 82044993d38SGabriel FERNANDEZ struct clk_composite *composite; 82144993d38SGabriel FERNANDEZ 82244993d38SGabriel FERNANDEZ if (!clk_data->clks[i]) 82344993d38SGabriel FERNANDEZ continue; 82444993d38SGabriel FERNANDEZ 8255fd9c05cSGeliang Tang composite = to_clk_composite(__clk_get_hw(clk_data->clks[i])); 8265fd9c05cSGeliang Tang kfree(to_clk_gate(composite->gate_hw)); 8275fd9c05cSGeliang Tang kfree(to_clk_divider(composite->rate_hw)); 8285fd9c05cSGeliang Tang kfree(to_clk_mux(composite->mux_hw)); 82944993d38SGabriel FERNANDEZ } 83044993d38SGabriel FERNANDEZ 83144993d38SGabriel FERNANDEZ kfree(clk_data->clks); 83286665d28SStephen Boyd err_alloc_clks: 83344993d38SGabriel FERNANDEZ kfree(clk_data); 83486665d28SStephen Boyd err_alloc: 83544993d38SGabriel FERNANDEZ kfree(parents); 83686665d28SStephen Boyd err_parents: 83786665d28SStephen Boyd iounmap(reg); 83844993d38SGabriel FERNANDEZ } 83944993d38SGabriel FERNANDEZ CLK_OF_DECLARE(clkgen_vcc, "st,clkgen-vcc", st_of_clkgen_vcc_setup); 840