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; 2794885fafSGabriel FERNANDEZ int nparents, i; 2894885fafSGabriel FERNANDEZ 2994885fafSGabriel FERNANDEZ nparents = of_count_phandle_with_args(np, "clocks", "#clock-cells"); 3094885fafSGabriel FERNANDEZ if (WARN_ON(nparents <= 0)) 3194885fafSGabriel FERNANDEZ return ERR_PTR(-EINVAL); 3294885fafSGabriel FERNANDEZ 3394885fafSGabriel FERNANDEZ parents = kzalloc(nparents * sizeof(const char *), GFP_KERNEL); 3494885fafSGabriel FERNANDEZ if (!parents) 3594885fafSGabriel FERNANDEZ return ERR_PTR(-ENOMEM); 3694885fafSGabriel FERNANDEZ 3794885fafSGabriel FERNANDEZ for (i = 0; i < nparents; i++) 3894885fafSGabriel FERNANDEZ parents[i] = of_clk_get_parent_name(np, i); 3994885fafSGabriel FERNANDEZ 4094885fafSGabriel FERNANDEZ *num_parents = nparents; 4194885fafSGabriel FERNANDEZ return parents; 4294885fafSGabriel FERNANDEZ } 4394885fafSGabriel FERNANDEZ 4494885fafSGabriel FERNANDEZ /** 4594885fafSGabriel FERNANDEZ * DOC: Clock mux with a programmable divider on each of its three inputs. 4694885fafSGabriel FERNANDEZ * The mux has an input setting which effectively gates its output. 4794885fafSGabriel FERNANDEZ * 4894885fafSGabriel FERNANDEZ * Traits of this clock: 4994885fafSGabriel FERNANDEZ * prepare - clk_(un)prepare only ensures parent is (un)prepared 5094885fafSGabriel FERNANDEZ * enable - clk_enable and clk_disable are functional & control gating 5194885fafSGabriel FERNANDEZ * rate - set rate is supported 5294885fafSGabriel FERNANDEZ * parent - set/get parent 5394885fafSGabriel FERNANDEZ */ 5494885fafSGabriel FERNANDEZ 5594885fafSGabriel FERNANDEZ #define NUM_INPUTS 3 5694885fafSGabriel FERNANDEZ 5794885fafSGabriel FERNANDEZ struct clkgena_divmux { 5894885fafSGabriel FERNANDEZ struct clk_hw hw; 5994885fafSGabriel FERNANDEZ /* Subclassed mux and divider structures */ 6094885fafSGabriel FERNANDEZ struct clk_mux mux; 6194885fafSGabriel FERNANDEZ struct clk_divider div[NUM_INPUTS]; 6294885fafSGabriel FERNANDEZ /* Enable/running feedback register bits for each input */ 6394885fafSGabriel FERNANDEZ void __iomem *feedback_reg[NUM_INPUTS]; 6494885fafSGabriel FERNANDEZ int feedback_bit_idx; 6594885fafSGabriel FERNANDEZ 6694885fafSGabriel FERNANDEZ u8 muxsel; 6794885fafSGabriel FERNANDEZ }; 6894885fafSGabriel FERNANDEZ 6994885fafSGabriel FERNANDEZ #define to_clkgena_divmux(_hw) container_of(_hw, struct clkgena_divmux, hw) 7094885fafSGabriel FERNANDEZ 7194885fafSGabriel FERNANDEZ struct clkgena_divmux_data { 7294885fafSGabriel FERNANDEZ int num_outputs; 7394885fafSGabriel FERNANDEZ int mux_offset; 7494885fafSGabriel FERNANDEZ int mux_offset2; 7594885fafSGabriel FERNANDEZ int mux_start_bit; 7694885fafSGabriel FERNANDEZ int div_offsets[NUM_INPUTS]; 7794885fafSGabriel FERNANDEZ int fb_offsets[NUM_INPUTS]; 7894885fafSGabriel FERNANDEZ int fb_start_bit_idx; 7994885fafSGabriel FERNANDEZ }; 8094885fafSGabriel FERNANDEZ 8194885fafSGabriel FERNANDEZ #define CKGAX_CLKOPSRC_SWITCH_OFF 0x3 8294885fafSGabriel FERNANDEZ 8394885fafSGabriel FERNANDEZ static int clkgena_divmux_is_running(struct clkgena_divmux *mux) 8494885fafSGabriel FERNANDEZ { 8594885fafSGabriel FERNANDEZ u32 regval = readl(mux->feedback_reg[mux->muxsel]); 8694885fafSGabriel FERNANDEZ u32 running = regval & BIT(mux->feedback_bit_idx); 8794885fafSGabriel FERNANDEZ return !!running; 8894885fafSGabriel FERNANDEZ } 8994885fafSGabriel FERNANDEZ 9094885fafSGabriel FERNANDEZ static int clkgena_divmux_enable(struct clk_hw *hw) 9194885fafSGabriel FERNANDEZ { 9294885fafSGabriel FERNANDEZ struct clkgena_divmux *genamux = to_clkgena_divmux(hw); 9394885fafSGabriel FERNANDEZ struct clk_hw *mux_hw = &genamux->mux.hw; 9494885fafSGabriel FERNANDEZ unsigned long timeout; 9594885fafSGabriel FERNANDEZ int ret = 0; 9694885fafSGabriel FERNANDEZ 9794885fafSGabriel FERNANDEZ mux_hw->clk = hw->clk; 9894885fafSGabriel FERNANDEZ 9994885fafSGabriel FERNANDEZ ret = clk_mux_ops.set_parent(mux_hw, genamux->muxsel); 10094885fafSGabriel FERNANDEZ if (ret) 10194885fafSGabriel FERNANDEZ return ret; 10294885fafSGabriel FERNANDEZ 10394885fafSGabriel FERNANDEZ timeout = jiffies + msecs_to_jiffies(10); 10494885fafSGabriel FERNANDEZ 10594885fafSGabriel FERNANDEZ while (!clkgena_divmux_is_running(genamux)) { 10694885fafSGabriel FERNANDEZ if (time_after(jiffies, timeout)) 10794885fafSGabriel FERNANDEZ return -ETIMEDOUT; 10894885fafSGabriel FERNANDEZ cpu_relax(); 10994885fafSGabriel FERNANDEZ } 11094885fafSGabriel FERNANDEZ 11194885fafSGabriel FERNANDEZ return 0; 11294885fafSGabriel FERNANDEZ } 11394885fafSGabriel FERNANDEZ 11494885fafSGabriel FERNANDEZ static void clkgena_divmux_disable(struct clk_hw *hw) 11594885fafSGabriel FERNANDEZ { 11694885fafSGabriel FERNANDEZ struct clkgena_divmux *genamux = to_clkgena_divmux(hw); 11794885fafSGabriel FERNANDEZ struct clk_hw *mux_hw = &genamux->mux.hw; 11894885fafSGabriel FERNANDEZ 11994885fafSGabriel FERNANDEZ mux_hw->clk = hw->clk; 12094885fafSGabriel FERNANDEZ 12194885fafSGabriel FERNANDEZ clk_mux_ops.set_parent(mux_hw, CKGAX_CLKOPSRC_SWITCH_OFF); 12294885fafSGabriel FERNANDEZ } 12394885fafSGabriel FERNANDEZ 12494885fafSGabriel FERNANDEZ static int clkgena_divmux_is_enabled(struct clk_hw *hw) 12594885fafSGabriel FERNANDEZ { 12694885fafSGabriel FERNANDEZ struct clkgena_divmux *genamux = to_clkgena_divmux(hw); 12794885fafSGabriel FERNANDEZ struct clk_hw *mux_hw = &genamux->mux.hw; 12894885fafSGabriel FERNANDEZ 12994885fafSGabriel FERNANDEZ mux_hw->clk = hw->clk; 13094885fafSGabriel FERNANDEZ 13194885fafSGabriel FERNANDEZ return (s8)clk_mux_ops.get_parent(mux_hw) > 0; 13294885fafSGabriel FERNANDEZ } 13394885fafSGabriel FERNANDEZ 13494885fafSGabriel FERNANDEZ u8 clkgena_divmux_get_parent(struct clk_hw *hw) 13594885fafSGabriel FERNANDEZ { 13694885fafSGabriel FERNANDEZ struct clkgena_divmux *genamux = to_clkgena_divmux(hw); 13794885fafSGabriel FERNANDEZ struct clk_hw *mux_hw = &genamux->mux.hw; 13894885fafSGabriel FERNANDEZ 13994885fafSGabriel FERNANDEZ mux_hw->clk = hw->clk; 14094885fafSGabriel FERNANDEZ 14194885fafSGabriel FERNANDEZ genamux->muxsel = clk_mux_ops.get_parent(mux_hw); 14294885fafSGabriel FERNANDEZ if ((s8)genamux->muxsel < 0) { 14394885fafSGabriel FERNANDEZ pr_debug("%s: %s: Invalid parent, setting to default.\n", 14494885fafSGabriel FERNANDEZ __func__, __clk_get_name(hw->clk)); 14594885fafSGabriel FERNANDEZ genamux->muxsel = 0; 14694885fafSGabriel FERNANDEZ } 14794885fafSGabriel FERNANDEZ 14894885fafSGabriel FERNANDEZ return genamux->muxsel; 14994885fafSGabriel FERNANDEZ } 15094885fafSGabriel FERNANDEZ 15194885fafSGabriel FERNANDEZ static int clkgena_divmux_set_parent(struct clk_hw *hw, u8 index) 15294885fafSGabriel FERNANDEZ { 15394885fafSGabriel FERNANDEZ struct clkgena_divmux *genamux = to_clkgena_divmux(hw); 15494885fafSGabriel FERNANDEZ 15594885fafSGabriel FERNANDEZ if (index >= CKGAX_CLKOPSRC_SWITCH_OFF) 15694885fafSGabriel FERNANDEZ return -EINVAL; 15794885fafSGabriel FERNANDEZ 15894885fafSGabriel FERNANDEZ genamux->muxsel = index; 15994885fafSGabriel FERNANDEZ 16094885fafSGabriel FERNANDEZ /* 16194885fafSGabriel FERNANDEZ * If the mux is already enabled, call enable directly to set the 16294885fafSGabriel FERNANDEZ * new mux position and wait for it to start running again. Otherwise 16394885fafSGabriel FERNANDEZ * do nothing. 16494885fafSGabriel FERNANDEZ */ 16594885fafSGabriel FERNANDEZ if (clkgena_divmux_is_enabled(hw)) 16694885fafSGabriel FERNANDEZ clkgena_divmux_enable(hw); 16794885fafSGabriel FERNANDEZ 16894885fafSGabriel FERNANDEZ return 0; 16994885fafSGabriel FERNANDEZ } 17094885fafSGabriel FERNANDEZ 17194885fafSGabriel FERNANDEZ unsigned long clkgena_divmux_recalc_rate(struct clk_hw *hw, 17294885fafSGabriel FERNANDEZ unsigned long parent_rate) 17394885fafSGabriel FERNANDEZ { 17494885fafSGabriel FERNANDEZ struct clkgena_divmux *genamux = to_clkgena_divmux(hw); 17594885fafSGabriel FERNANDEZ struct clk_hw *div_hw = &genamux->div[genamux->muxsel].hw; 17694885fafSGabriel FERNANDEZ 17794885fafSGabriel FERNANDEZ div_hw->clk = hw->clk; 17894885fafSGabriel FERNANDEZ 17994885fafSGabriel FERNANDEZ return clk_divider_ops.recalc_rate(div_hw, parent_rate); 18094885fafSGabriel FERNANDEZ } 18194885fafSGabriel FERNANDEZ 18294885fafSGabriel FERNANDEZ static int clkgena_divmux_set_rate(struct clk_hw *hw, unsigned long rate, 18394885fafSGabriel FERNANDEZ unsigned long parent_rate) 18494885fafSGabriel FERNANDEZ { 18594885fafSGabriel FERNANDEZ struct clkgena_divmux *genamux = to_clkgena_divmux(hw); 18694885fafSGabriel FERNANDEZ struct clk_hw *div_hw = &genamux->div[genamux->muxsel].hw; 18794885fafSGabriel FERNANDEZ 18894885fafSGabriel FERNANDEZ div_hw->clk = hw->clk; 18994885fafSGabriel FERNANDEZ 19094885fafSGabriel FERNANDEZ return clk_divider_ops.set_rate(div_hw, rate, parent_rate); 19194885fafSGabriel FERNANDEZ } 19294885fafSGabriel FERNANDEZ 19394885fafSGabriel FERNANDEZ static long clkgena_divmux_round_rate(struct clk_hw *hw, unsigned long rate, 19494885fafSGabriel FERNANDEZ unsigned long *prate) 19594885fafSGabriel FERNANDEZ { 19694885fafSGabriel FERNANDEZ struct clkgena_divmux *genamux = to_clkgena_divmux(hw); 19794885fafSGabriel FERNANDEZ struct clk_hw *div_hw = &genamux->div[genamux->muxsel].hw; 19894885fafSGabriel FERNANDEZ 19994885fafSGabriel FERNANDEZ div_hw->clk = hw->clk; 20094885fafSGabriel FERNANDEZ 20194885fafSGabriel FERNANDEZ return clk_divider_ops.round_rate(div_hw, rate, prate); 20294885fafSGabriel FERNANDEZ } 20394885fafSGabriel FERNANDEZ 20494885fafSGabriel FERNANDEZ static const struct clk_ops clkgena_divmux_ops = { 20594885fafSGabriel FERNANDEZ .enable = clkgena_divmux_enable, 20694885fafSGabriel FERNANDEZ .disable = clkgena_divmux_disable, 20794885fafSGabriel FERNANDEZ .is_enabled = clkgena_divmux_is_enabled, 20894885fafSGabriel FERNANDEZ .get_parent = clkgena_divmux_get_parent, 20994885fafSGabriel FERNANDEZ .set_parent = clkgena_divmux_set_parent, 21094885fafSGabriel FERNANDEZ .round_rate = clkgena_divmux_round_rate, 21194885fafSGabriel FERNANDEZ .recalc_rate = clkgena_divmux_recalc_rate, 21294885fafSGabriel FERNANDEZ .set_rate = clkgena_divmux_set_rate, 21394885fafSGabriel FERNANDEZ }; 21494885fafSGabriel FERNANDEZ 21594885fafSGabriel FERNANDEZ /** 21694885fafSGabriel FERNANDEZ * clk_register_genamux - register a genamux clock with the clock framework 21794885fafSGabriel FERNANDEZ */ 21894885fafSGabriel FERNANDEZ struct clk *clk_register_genamux(const char *name, 21994885fafSGabriel FERNANDEZ const char **parent_names, u8 num_parents, 22094885fafSGabriel FERNANDEZ void __iomem *reg, 22194885fafSGabriel FERNANDEZ const struct clkgena_divmux_data *muxdata, 22294885fafSGabriel FERNANDEZ u32 idx) 22394885fafSGabriel FERNANDEZ { 22494885fafSGabriel FERNANDEZ /* 22594885fafSGabriel FERNANDEZ * Fixed constants across all ClockgenA variants 22694885fafSGabriel FERNANDEZ */ 22794885fafSGabriel FERNANDEZ const int mux_width = 2; 22894885fafSGabriel FERNANDEZ const int divider_width = 5; 22994885fafSGabriel FERNANDEZ struct clkgena_divmux *genamux; 23094885fafSGabriel FERNANDEZ struct clk *clk; 23194885fafSGabriel FERNANDEZ struct clk_init_data init; 23294885fafSGabriel FERNANDEZ int i; 23394885fafSGabriel FERNANDEZ 23494885fafSGabriel FERNANDEZ genamux = kzalloc(sizeof(*genamux), GFP_KERNEL); 23594885fafSGabriel FERNANDEZ if (!genamux) 23694885fafSGabriel FERNANDEZ return ERR_PTR(-ENOMEM); 23794885fafSGabriel FERNANDEZ 23894885fafSGabriel FERNANDEZ init.name = name; 23994885fafSGabriel FERNANDEZ init.ops = &clkgena_divmux_ops; 24094885fafSGabriel FERNANDEZ init.flags = CLK_IS_BASIC; 24194885fafSGabriel FERNANDEZ init.parent_names = parent_names; 24294885fafSGabriel FERNANDEZ init.num_parents = num_parents; 24394885fafSGabriel FERNANDEZ 24494885fafSGabriel FERNANDEZ genamux->mux.lock = &clkgena_divmux_lock; 24594885fafSGabriel FERNANDEZ genamux->mux.mask = BIT(mux_width) - 1; 24694885fafSGabriel FERNANDEZ genamux->mux.shift = muxdata->mux_start_bit + (idx * mux_width); 24794885fafSGabriel FERNANDEZ if (genamux->mux.shift > 31) { 24894885fafSGabriel FERNANDEZ /* 24994885fafSGabriel FERNANDEZ * We have spilled into the second mux register so 25094885fafSGabriel FERNANDEZ * adjust the register address and the bit shift accordingly 25194885fafSGabriel FERNANDEZ */ 25294885fafSGabriel FERNANDEZ genamux->mux.reg = reg + muxdata->mux_offset2; 25394885fafSGabriel FERNANDEZ genamux->mux.shift -= 32; 25494885fafSGabriel FERNANDEZ } else { 25594885fafSGabriel FERNANDEZ genamux->mux.reg = reg + muxdata->mux_offset; 25694885fafSGabriel FERNANDEZ } 25794885fafSGabriel FERNANDEZ 25894885fafSGabriel FERNANDEZ for (i = 0; i < NUM_INPUTS; i++) { 25994885fafSGabriel FERNANDEZ /* 26094885fafSGabriel FERNANDEZ * Divider config for each input 26194885fafSGabriel FERNANDEZ */ 26294885fafSGabriel FERNANDEZ void __iomem *divbase = reg + muxdata->div_offsets[i]; 26394885fafSGabriel FERNANDEZ genamux->div[i].width = divider_width; 26494885fafSGabriel FERNANDEZ genamux->div[i].reg = divbase + (idx * sizeof(u32)); 26594885fafSGabriel FERNANDEZ 26694885fafSGabriel FERNANDEZ /* 26794885fafSGabriel FERNANDEZ * Mux enabled/running feedback register for each input. 26894885fafSGabriel FERNANDEZ */ 26994885fafSGabriel FERNANDEZ genamux->feedback_reg[i] = reg + muxdata->fb_offsets[i]; 27094885fafSGabriel FERNANDEZ } 27194885fafSGabriel FERNANDEZ 27294885fafSGabriel FERNANDEZ genamux->feedback_bit_idx = muxdata->fb_start_bit_idx + idx; 27394885fafSGabriel FERNANDEZ genamux->hw.init = &init; 27494885fafSGabriel FERNANDEZ 27594885fafSGabriel FERNANDEZ clk = clk_register(NULL, &genamux->hw); 27694885fafSGabriel FERNANDEZ if (IS_ERR(clk)) { 27794885fafSGabriel FERNANDEZ kfree(genamux); 27894885fafSGabriel FERNANDEZ goto err; 27994885fafSGabriel FERNANDEZ } 28094885fafSGabriel FERNANDEZ 28194885fafSGabriel FERNANDEZ pr_debug("%s: parent %s rate %lu\n", 28294885fafSGabriel FERNANDEZ __clk_get_name(clk), 28394885fafSGabriel FERNANDEZ __clk_get_name(clk_get_parent(clk)), 28494885fafSGabriel FERNANDEZ clk_get_rate(clk)); 28594885fafSGabriel FERNANDEZ err: 28694885fafSGabriel FERNANDEZ return clk; 28794885fafSGabriel FERNANDEZ } 28894885fafSGabriel FERNANDEZ 28994885fafSGabriel FERNANDEZ static struct clkgena_divmux_data st_divmux_c65hs = { 29094885fafSGabriel FERNANDEZ .num_outputs = 4, 29194885fafSGabriel FERNANDEZ .mux_offset = 0x14, 29294885fafSGabriel FERNANDEZ .mux_start_bit = 0, 29394885fafSGabriel FERNANDEZ .div_offsets = { 0x800, 0x900, 0xb00 }, 29494885fafSGabriel FERNANDEZ .fb_offsets = { 0x18, 0x1c, 0x20 }, 29594885fafSGabriel FERNANDEZ .fb_start_bit_idx = 0, 29694885fafSGabriel FERNANDEZ }; 29794885fafSGabriel FERNANDEZ 29894885fafSGabriel FERNANDEZ static struct clkgena_divmux_data st_divmux_c65ls = { 29994885fafSGabriel FERNANDEZ .num_outputs = 14, 30094885fafSGabriel FERNANDEZ .mux_offset = 0x14, 30194885fafSGabriel FERNANDEZ .mux_offset2 = 0x24, 30294885fafSGabriel FERNANDEZ .mux_start_bit = 8, 30394885fafSGabriel FERNANDEZ .div_offsets = { 0x810, 0xa10, 0xb10 }, 30494885fafSGabriel FERNANDEZ .fb_offsets = { 0x18, 0x1c, 0x20 }, 30594885fafSGabriel FERNANDEZ .fb_start_bit_idx = 4, 30694885fafSGabriel FERNANDEZ }; 30794885fafSGabriel FERNANDEZ 30894885fafSGabriel FERNANDEZ static struct clkgena_divmux_data st_divmux_c32odf0 = { 30994885fafSGabriel FERNANDEZ .num_outputs = 8, 31094885fafSGabriel FERNANDEZ .mux_offset = 0x1c, 31194885fafSGabriel FERNANDEZ .mux_start_bit = 0, 31294885fafSGabriel FERNANDEZ .div_offsets = { 0x800, 0x900, 0xa60 }, 31394885fafSGabriel FERNANDEZ .fb_offsets = { 0x2c, 0x24, 0x28 }, 31494885fafSGabriel FERNANDEZ .fb_start_bit_idx = 0, 31594885fafSGabriel FERNANDEZ }; 31694885fafSGabriel FERNANDEZ 31794885fafSGabriel FERNANDEZ static struct clkgena_divmux_data st_divmux_c32odf1 = { 31894885fafSGabriel FERNANDEZ .num_outputs = 8, 31994885fafSGabriel FERNANDEZ .mux_offset = 0x1c, 32094885fafSGabriel FERNANDEZ .mux_start_bit = 16, 32194885fafSGabriel FERNANDEZ .div_offsets = { 0x820, 0x980, 0xa80 }, 32294885fafSGabriel FERNANDEZ .fb_offsets = { 0x2c, 0x24, 0x28 }, 32394885fafSGabriel FERNANDEZ .fb_start_bit_idx = 8, 32494885fafSGabriel FERNANDEZ }; 32594885fafSGabriel FERNANDEZ 32694885fafSGabriel FERNANDEZ static struct clkgena_divmux_data st_divmux_c32odf2 = { 32794885fafSGabriel FERNANDEZ .num_outputs = 8, 32894885fafSGabriel FERNANDEZ .mux_offset = 0x20, 32994885fafSGabriel FERNANDEZ .mux_start_bit = 0, 33094885fafSGabriel FERNANDEZ .div_offsets = { 0x840, 0xa20, 0xb10 }, 33194885fafSGabriel FERNANDEZ .fb_offsets = { 0x2c, 0x24, 0x28 }, 33294885fafSGabriel FERNANDEZ .fb_start_bit_idx = 16, 33394885fafSGabriel FERNANDEZ }; 33494885fafSGabriel FERNANDEZ 33594885fafSGabriel FERNANDEZ static struct clkgena_divmux_data st_divmux_c32odf3 = { 33694885fafSGabriel FERNANDEZ .num_outputs = 8, 33794885fafSGabriel FERNANDEZ .mux_offset = 0x20, 33894885fafSGabriel FERNANDEZ .mux_start_bit = 16, 33994885fafSGabriel FERNANDEZ .div_offsets = { 0x860, 0xa40, 0xb30 }, 34094885fafSGabriel FERNANDEZ .fb_offsets = { 0x2c, 0x24, 0x28 }, 34194885fafSGabriel FERNANDEZ .fb_start_bit_idx = 24, 34294885fafSGabriel FERNANDEZ }; 34394885fafSGabriel FERNANDEZ 34494885fafSGabriel FERNANDEZ static struct of_device_id clkgena_divmux_of_match[] = { 34594885fafSGabriel FERNANDEZ { 34694885fafSGabriel FERNANDEZ .compatible = "st,clkgena-divmux-c65-hs", 34794885fafSGabriel FERNANDEZ .data = &st_divmux_c65hs, 34894885fafSGabriel FERNANDEZ }, 34994885fafSGabriel FERNANDEZ { 35094885fafSGabriel FERNANDEZ .compatible = "st,clkgena-divmux-c65-ls", 35194885fafSGabriel FERNANDEZ .data = &st_divmux_c65ls, 35294885fafSGabriel FERNANDEZ }, 35394885fafSGabriel FERNANDEZ { 35494885fafSGabriel FERNANDEZ .compatible = "st,clkgena-divmux-c32-odf0", 35594885fafSGabriel FERNANDEZ .data = &st_divmux_c32odf0, 35694885fafSGabriel FERNANDEZ }, 35794885fafSGabriel FERNANDEZ { 35894885fafSGabriel FERNANDEZ .compatible = "st,clkgena-divmux-c32-odf1", 35994885fafSGabriel FERNANDEZ .data = &st_divmux_c32odf1, 36094885fafSGabriel FERNANDEZ }, 36194885fafSGabriel FERNANDEZ { 36294885fafSGabriel FERNANDEZ .compatible = "st,clkgena-divmux-c32-odf2", 36394885fafSGabriel FERNANDEZ .data = &st_divmux_c32odf2, 36494885fafSGabriel FERNANDEZ }, 36594885fafSGabriel FERNANDEZ { 36694885fafSGabriel FERNANDEZ .compatible = "st,clkgena-divmux-c32-odf3", 36794885fafSGabriel FERNANDEZ .data = &st_divmux_c32odf3, 36894885fafSGabriel FERNANDEZ }, 36994885fafSGabriel FERNANDEZ {} 37094885fafSGabriel FERNANDEZ }; 37194885fafSGabriel FERNANDEZ 37294885fafSGabriel FERNANDEZ static void __iomem * __init clkgen_get_register_base( 37394885fafSGabriel FERNANDEZ struct device_node *np) 37494885fafSGabriel FERNANDEZ { 37594885fafSGabriel FERNANDEZ struct device_node *pnode; 37694885fafSGabriel FERNANDEZ void __iomem *reg = NULL; 37794885fafSGabriel FERNANDEZ 37894885fafSGabriel FERNANDEZ pnode = of_get_parent(np); 37994885fafSGabriel FERNANDEZ if (!pnode) 38094885fafSGabriel FERNANDEZ return NULL; 38194885fafSGabriel FERNANDEZ 38294885fafSGabriel FERNANDEZ reg = of_iomap(pnode, 0); 38394885fafSGabriel FERNANDEZ 38494885fafSGabriel FERNANDEZ of_node_put(pnode); 38594885fafSGabriel FERNANDEZ return reg; 38694885fafSGabriel FERNANDEZ } 38794885fafSGabriel FERNANDEZ 38894885fafSGabriel FERNANDEZ void __init st_of_clkgena_divmux_setup(struct device_node *np) 38994885fafSGabriel FERNANDEZ { 39094885fafSGabriel FERNANDEZ const struct of_device_id *match; 39194885fafSGabriel FERNANDEZ const struct clkgena_divmux_data *data; 39294885fafSGabriel FERNANDEZ struct clk_onecell_data *clk_data; 39394885fafSGabriel FERNANDEZ void __iomem *reg; 39494885fafSGabriel FERNANDEZ const char **parents; 39594885fafSGabriel FERNANDEZ int num_parents = 0, i; 39694885fafSGabriel FERNANDEZ 39794885fafSGabriel FERNANDEZ match = of_match_node(clkgena_divmux_of_match, np); 39894885fafSGabriel FERNANDEZ if (WARN_ON(!match)) 39994885fafSGabriel FERNANDEZ return; 40094885fafSGabriel FERNANDEZ 40194885fafSGabriel FERNANDEZ data = (struct clkgena_divmux_data *)match->data; 40294885fafSGabriel FERNANDEZ 40394885fafSGabriel FERNANDEZ reg = clkgen_get_register_base(np); 40494885fafSGabriel FERNANDEZ if (!reg) 40594885fafSGabriel FERNANDEZ return; 40694885fafSGabriel FERNANDEZ 40794885fafSGabriel FERNANDEZ parents = clkgen_mux_get_parents(np, &num_parents); 40894885fafSGabriel FERNANDEZ if (IS_ERR(parents)) 40994885fafSGabriel FERNANDEZ return; 41094885fafSGabriel FERNANDEZ 41194885fafSGabriel FERNANDEZ clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL); 41294885fafSGabriel FERNANDEZ if (!clk_data) 41394885fafSGabriel FERNANDEZ goto err; 41494885fafSGabriel FERNANDEZ 41594885fafSGabriel FERNANDEZ clk_data->clk_num = data->num_outputs; 41694885fafSGabriel FERNANDEZ clk_data->clks = kzalloc(clk_data->clk_num * sizeof(struct clk *), 41794885fafSGabriel FERNANDEZ GFP_KERNEL); 41894885fafSGabriel FERNANDEZ 41994885fafSGabriel FERNANDEZ if (!clk_data->clks) 42094885fafSGabriel FERNANDEZ goto err; 42194885fafSGabriel FERNANDEZ 42294885fafSGabriel FERNANDEZ for (i = 0; i < clk_data->clk_num; i++) { 42394885fafSGabriel FERNANDEZ struct clk *clk; 42494885fafSGabriel FERNANDEZ const char *clk_name; 42594885fafSGabriel FERNANDEZ 42694885fafSGabriel FERNANDEZ if (of_property_read_string_index(np, "clock-output-names", 42794885fafSGabriel FERNANDEZ i, &clk_name)) 42894885fafSGabriel FERNANDEZ break; 42994885fafSGabriel FERNANDEZ 43094885fafSGabriel FERNANDEZ /* 43194885fafSGabriel FERNANDEZ * If we read an empty clock name then the output is unused 43294885fafSGabriel FERNANDEZ */ 43394885fafSGabriel FERNANDEZ if (*clk_name == '\0') 43494885fafSGabriel FERNANDEZ continue; 43594885fafSGabriel FERNANDEZ 43694885fafSGabriel FERNANDEZ clk = clk_register_genamux(clk_name, parents, num_parents, 43794885fafSGabriel FERNANDEZ reg, data, i); 43894885fafSGabriel FERNANDEZ 43994885fafSGabriel FERNANDEZ if (IS_ERR(clk)) 44094885fafSGabriel FERNANDEZ goto err; 44194885fafSGabriel FERNANDEZ 44294885fafSGabriel FERNANDEZ clk_data->clks[i] = clk; 44394885fafSGabriel FERNANDEZ } 44494885fafSGabriel FERNANDEZ 44594885fafSGabriel FERNANDEZ kfree(parents); 44694885fafSGabriel FERNANDEZ 44794885fafSGabriel FERNANDEZ of_clk_add_provider(np, of_clk_src_onecell_get, clk_data); 44894885fafSGabriel FERNANDEZ return; 44994885fafSGabriel FERNANDEZ err: 45094885fafSGabriel FERNANDEZ if (clk_data) 45194885fafSGabriel FERNANDEZ kfree(clk_data->clks); 45294885fafSGabriel FERNANDEZ 45394885fafSGabriel FERNANDEZ kfree(clk_data); 45494885fafSGabriel FERNANDEZ kfree(parents); 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 48294885fafSGabriel FERNANDEZ static 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 48894885fafSGabriel FERNANDEZ 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; 49494885fafSGabriel FERNANDEZ 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 50294885fafSGabriel FERNANDEZ data = (struct clkgena_prediv_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) 51094885fafSGabriel FERNANDEZ return; 51194885fafSGabriel FERNANDEZ 51294885fafSGabriel FERNANDEZ if (of_property_read_string_index(np, "clock-output-names", 51394885fafSGabriel FERNANDEZ 0, &clk_name)) 51494885fafSGabriel FERNANDEZ return; 51594885fafSGabriel FERNANDEZ 51694885fafSGabriel FERNANDEZ clk = clk_register_divider_table(NULL, clk_name, parent_name, 0, 51794885fafSGabriel FERNANDEZ reg + data->offset, data->shift, 1, 51894885fafSGabriel FERNANDEZ 0, data->table, NULL); 51994885fafSGabriel FERNANDEZ if (IS_ERR(clk)) 52094885fafSGabriel FERNANDEZ return; 52194885fafSGabriel FERNANDEZ 52294885fafSGabriel FERNANDEZ of_clk_add_provider(np, of_clk_src_simple_get, clk); 52394885fafSGabriel FERNANDEZ pr_debug("%s: parent %s rate %u\n", 52494885fafSGabriel FERNANDEZ __clk_get_name(clk), 52594885fafSGabriel FERNANDEZ __clk_get_name(clk_get_parent(clk)), 52694885fafSGabriel FERNANDEZ (unsigned int)clk_get_rate(clk)); 52794885fafSGabriel FERNANDEZ 52894885fafSGabriel FERNANDEZ return; 52994885fafSGabriel FERNANDEZ } 53094885fafSGabriel FERNANDEZ CLK_OF_DECLARE(clkgenaprediv, "st,clkgena-prediv", st_of_clkgena_prediv_setup); 53144993d38SGabriel FERNANDEZ 53244993d38SGabriel FERNANDEZ struct clkgen_mux_data { 53344993d38SGabriel FERNANDEZ u32 offset; 53444993d38SGabriel FERNANDEZ u8 shift; 53544993d38SGabriel FERNANDEZ u8 width; 53644993d38SGabriel FERNANDEZ spinlock_t *lock; 53744993d38SGabriel FERNANDEZ unsigned long clk_flags; 53844993d38SGabriel FERNANDEZ u8 mux_flags; 53944993d38SGabriel FERNANDEZ }; 54044993d38SGabriel FERNANDEZ 54144993d38SGabriel FERNANDEZ static struct clkgen_mux_data clkgen_mux_c_vcc_hd_416 = { 54244993d38SGabriel FERNANDEZ .offset = 0, 54344993d38SGabriel FERNANDEZ .shift = 0, 54444993d38SGabriel FERNANDEZ .width = 1, 54544993d38SGabriel FERNANDEZ }; 54644993d38SGabriel FERNANDEZ 54744993d38SGabriel FERNANDEZ static struct clkgen_mux_data clkgen_mux_f_vcc_fvdp_416 = { 54844993d38SGabriel FERNANDEZ .offset = 0, 54944993d38SGabriel FERNANDEZ .shift = 0, 55044993d38SGabriel FERNANDEZ .width = 1, 55144993d38SGabriel FERNANDEZ }; 55244993d38SGabriel FERNANDEZ 55344993d38SGabriel FERNANDEZ static struct clkgen_mux_data clkgen_mux_f_vcc_hva_416 = { 55444993d38SGabriel FERNANDEZ .offset = 0, 55544993d38SGabriel FERNANDEZ .shift = 0, 55644993d38SGabriel FERNANDEZ .width = 1, 55744993d38SGabriel FERNANDEZ }; 55844993d38SGabriel FERNANDEZ 55944993d38SGabriel FERNANDEZ static struct clkgen_mux_data clkgen_mux_f_vcc_hd_416 = { 56044993d38SGabriel FERNANDEZ .offset = 0, 56144993d38SGabriel FERNANDEZ .shift = 16, 56244993d38SGabriel FERNANDEZ .width = 1, 56344993d38SGabriel FERNANDEZ .lock = &clkgenf_lock, 56444993d38SGabriel FERNANDEZ }; 56544993d38SGabriel FERNANDEZ 56644993d38SGabriel FERNANDEZ static struct clkgen_mux_data clkgen_mux_c_vcc_sd_416 = { 56744993d38SGabriel FERNANDEZ .offset = 0, 56844993d38SGabriel FERNANDEZ .shift = 17, 56944993d38SGabriel FERNANDEZ .width = 1, 57044993d38SGabriel FERNANDEZ .lock = &clkgenf_lock, 57144993d38SGabriel FERNANDEZ }; 57244993d38SGabriel FERNANDEZ 573*ab35dc13SGabriel FERNANDEZ static struct clkgen_mux_data stih415_a9_mux_data = { 574*ab35dc13SGabriel FERNANDEZ .offset = 0, 575*ab35dc13SGabriel FERNANDEZ .shift = 1, 576*ab35dc13SGabriel FERNANDEZ .width = 2, 577*ab35dc13SGabriel FERNANDEZ }; 578*ab35dc13SGabriel FERNANDEZ static struct clkgen_mux_data stih416_a9_mux_data = { 579*ab35dc13SGabriel FERNANDEZ .offset = 0, 580*ab35dc13SGabriel FERNANDEZ .shift = 0, 581*ab35dc13SGabriel FERNANDEZ .width = 2, 582*ab35dc13SGabriel FERNANDEZ }; 583*ab35dc13SGabriel FERNANDEZ 58444993d38SGabriel FERNANDEZ static struct of_device_id mux_of_match[] = { 58544993d38SGabriel FERNANDEZ { 58644993d38SGabriel FERNANDEZ .compatible = "st,stih416-clkgenc-vcc-hd", 58744993d38SGabriel FERNANDEZ .data = &clkgen_mux_c_vcc_hd_416, 58844993d38SGabriel FERNANDEZ }, 58944993d38SGabriel FERNANDEZ { 59044993d38SGabriel FERNANDEZ .compatible = "st,stih416-clkgenf-vcc-fvdp", 59144993d38SGabriel FERNANDEZ .data = &clkgen_mux_f_vcc_fvdp_416, 59244993d38SGabriel FERNANDEZ }, 59344993d38SGabriel FERNANDEZ { 59444993d38SGabriel FERNANDEZ .compatible = "st,stih416-clkgenf-vcc-hva", 59544993d38SGabriel FERNANDEZ .data = &clkgen_mux_f_vcc_hva_416, 59644993d38SGabriel FERNANDEZ }, 59744993d38SGabriel FERNANDEZ { 59844993d38SGabriel FERNANDEZ .compatible = "st,stih416-clkgenf-vcc-hd", 59944993d38SGabriel FERNANDEZ .data = &clkgen_mux_f_vcc_hd_416, 60044993d38SGabriel FERNANDEZ }, 60144993d38SGabriel FERNANDEZ { 60244993d38SGabriel FERNANDEZ .compatible = "st,stih416-clkgenf-vcc-sd", 60344993d38SGabriel FERNANDEZ .data = &clkgen_mux_c_vcc_sd_416, 60444993d38SGabriel FERNANDEZ }, 605*ab35dc13SGabriel FERNANDEZ { 606*ab35dc13SGabriel FERNANDEZ .compatible = "st,stih415-clkgen-a9-mux", 607*ab35dc13SGabriel FERNANDEZ .data = &stih415_a9_mux_data, 608*ab35dc13SGabriel FERNANDEZ }, 609*ab35dc13SGabriel FERNANDEZ { 610*ab35dc13SGabriel FERNANDEZ .compatible = "st,stih416-clkgen-a9-mux", 611*ab35dc13SGabriel FERNANDEZ .data = &stih416_a9_mux_data, 612*ab35dc13SGabriel FERNANDEZ }, 61344993d38SGabriel FERNANDEZ {} 61444993d38SGabriel FERNANDEZ }; 61544993d38SGabriel FERNANDEZ 61644993d38SGabriel FERNANDEZ void __init st_of_clkgen_mux_setup(struct device_node *np) 61744993d38SGabriel FERNANDEZ { 61844993d38SGabriel FERNANDEZ const struct of_device_id *match; 61944993d38SGabriel FERNANDEZ struct clk *clk; 62044993d38SGabriel FERNANDEZ void __iomem *reg; 62144993d38SGabriel FERNANDEZ const char **parents; 62244993d38SGabriel FERNANDEZ int num_parents; 62344993d38SGabriel FERNANDEZ struct clkgen_mux_data *data; 62444993d38SGabriel FERNANDEZ 62544993d38SGabriel FERNANDEZ match = of_match_node(mux_of_match, np); 62644993d38SGabriel FERNANDEZ if (!match) { 62744993d38SGabriel FERNANDEZ pr_err("%s: No matching data\n", __func__); 62844993d38SGabriel FERNANDEZ return; 62944993d38SGabriel FERNANDEZ } 63044993d38SGabriel FERNANDEZ 63144993d38SGabriel FERNANDEZ data = (struct clkgen_mux_data *)match->data; 63244993d38SGabriel FERNANDEZ 63344993d38SGabriel FERNANDEZ reg = of_iomap(np, 0); 63444993d38SGabriel FERNANDEZ if (!reg) { 63544993d38SGabriel FERNANDEZ pr_err("%s: Failed to get base address\n", __func__); 63644993d38SGabriel FERNANDEZ return; 63744993d38SGabriel FERNANDEZ } 63844993d38SGabriel FERNANDEZ 63944993d38SGabriel FERNANDEZ parents = clkgen_mux_get_parents(np, &num_parents); 64044993d38SGabriel FERNANDEZ if (IS_ERR(parents)) { 64144993d38SGabriel FERNANDEZ pr_err("%s: Failed to get parents (%ld)\n", 64244993d38SGabriel FERNANDEZ __func__, PTR_ERR(parents)); 64344993d38SGabriel FERNANDEZ return; 64444993d38SGabriel FERNANDEZ } 64544993d38SGabriel FERNANDEZ 64644993d38SGabriel FERNANDEZ clk = clk_register_mux(NULL, np->name, parents, num_parents, 64744993d38SGabriel FERNANDEZ data->clk_flags | CLK_SET_RATE_PARENT, 64844993d38SGabriel FERNANDEZ reg + data->offset, 64944993d38SGabriel FERNANDEZ data->shift, data->width, data->mux_flags, 65044993d38SGabriel FERNANDEZ data->lock); 65144993d38SGabriel FERNANDEZ if (IS_ERR(clk)) 65244993d38SGabriel FERNANDEZ goto err; 65344993d38SGabriel FERNANDEZ 65444993d38SGabriel FERNANDEZ pr_debug("%s: parent %s rate %u\n", 65544993d38SGabriel FERNANDEZ __clk_get_name(clk), 65644993d38SGabriel FERNANDEZ __clk_get_name(clk_get_parent(clk)), 65744993d38SGabriel FERNANDEZ (unsigned int)clk_get_rate(clk)); 65844993d38SGabriel FERNANDEZ 65944993d38SGabriel FERNANDEZ of_clk_add_provider(np, of_clk_src_simple_get, clk); 66044993d38SGabriel FERNANDEZ 66144993d38SGabriel FERNANDEZ err: 66244993d38SGabriel FERNANDEZ kfree(parents); 66344993d38SGabriel FERNANDEZ 66444993d38SGabriel FERNANDEZ return; 66544993d38SGabriel FERNANDEZ } 66644993d38SGabriel FERNANDEZ CLK_OF_DECLARE(clkgen_mux, "st,clkgen-mux", st_of_clkgen_mux_setup); 66744993d38SGabriel FERNANDEZ 66844993d38SGabriel FERNANDEZ #define VCC_MAX_CHANNELS 16 66944993d38SGabriel FERNANDEZ 67044993d38SGabriel FERNANDEZ #define VCC_GATE_OFFSET 0x0 67144993d38SGabriel FERNANDEZ #define VCC_MUX_OFFSET 0x4 67244993d38SGabriel FERNANDEZ #define VCC_DIV_OFFSET 0x8 67344993d38SGabriel FERNANDEZ 67444993d38SGabriel FERNANDEZ struct clkgen_vcc_data { 67544993d38SGabriel FERNANDEZ spinlock_t *lock; 67644993d38SGabriel FERNANDEZ unsigned long clk_flags; 67744993d38SGabriel FERNANDEZ }; 67844993d38SGabriel FERNANDEZ 67944993d38SGabriel FERNANDEZ static struct clkgen_vcc_data st_clkgenc_vcc_416 = { 68044993d38SGabriel FERNANDEZ .clk_flags = CLK_SET_RATE_PARENT, 68144993d38SGabriel FERNANDEZ }; 68244993d38SGabriel FERNANDEZ 68344993d38SGabriel FERNANDEZ static struct clkgen_vcc_data st_clkgenf_vcc_416 = { 68444993d38SGabriel FERNANDEZ .lock = &clkgenf_lock, 68544993d38SGabriel FERNANDEZ }; 68644993d38SGabriel FERNANDEZ 68744993d38SGabriel FERNANDEZ static struct of_device_id vcc_of_match[] = { 68844993d38SGabriel FERNANDEZ { .compatible = "st,stih416-clkgenc", .data = &st_clkgenc_vcc_416 }, 68944993d38SGabriel FERNANDEZ { .compatible = "st,stih416-clkgenf", .data = &st_clkgenf_vcc_416 }, 69044993d38SGabriel FERNANDEZ {} 69144993d38SGabriel FERNANDEZ }; 69244993d38SGabriel FERNANDEZ 69344993d38SGabriel FERNANDEZ void __init st_of_clkgen_vcc_setup(struct device_node *np) 69444993d38SGabriel FERNANDEZ { 69544993d38SGabriel FERNANDEZ const struct of_device_id *match; 69644993d38SGabriel FERNANDEZ void __iomem *reg; 69744993d38SGabriel FERNANDEZ const char **parents; 69844993d38SGabriel FERNANDEZ int num_parents, i; 69944993d38SGabriel FERNANDEZ struct clk_onecell_data *clk_data; 70044993d38SGabriel FERNANDEZ struct clkgen_vcc_data *data; 70144993d38SGabriel FERNANDEZ 70244993d38SGabriel FERNANDEZ match = of_match_node(vcc_of_match, np); 70344993d38SGabriel FERNANDEZ if (WARN_ON(!match)) 70444993d38SGabriel FERNANDEZ return; 70544993d38SGabriel FERNANDEZ data = (struct clkgen_vcc_data *)match->data; 70644993d38SGabriel FERNANDEZ 70744993d38SGabriel FERNANDEZ reg = of_iomap(np, 0); 70844993d38SGabriel FERNANDEZ if (!reg) 70944993d38SGabriel FERNANDEZ return; 71044993d38SGabriel FERNANDEZ 71144993d38SGabriel FERNANDEZ parents = clkgen_mux_get_parents(np, &num_parents); 71244993d38SGabriel FERNANDEZ if (IS_ERR(parents)) 71344993d38SGabriel FERNANDEZ return; 71444993d38SGabriel FERNANDEZ 71544993d38SGabriel FERNANDEZ clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL); 71644993d38SGabriel FERNANDEZ if (!clk_data) 71744993d38SGabriel FERNANDEZ goto err; 71844993d38SGabriel FERNANDEZ 71944993d38SGabriel FERNANDEZ clk_data->clk_num = VCC_MAX_CHANNELS; 72044993d38SGabriel FERNANDEZ clk_data->clks = kzalloc(clk_data->clk_num * sizeof(struct clk *), 72144993d38SGabriel FERNANDEZ GFP_KERNEL); 72244993d38SGabriel FERNANDEZ 72344993d38SGabriel FERNANDEZ if (!clk_data->clks) 72444993d38SGabriel FERNANDEZ goto err; 72544993d38SGabriel FERNANDEZ 72644993d38SGabriel FERNANDEZ for (i = 0; i < clk_data->clk_num; i++) { 72744993d38SGabriel FERNANDEZ struct clk *clk; 72844993d38SGabriel FERNANDEZ const char *clk_name; 72944993d38SGabriel FERNANDEZ struct clk_gate *gate; 73044993d38SGabriel FERNANDEZ struct clk_divider *div; 73144993d38SGabriel FERNANDEZ struct clk_mux *mux; 73244993d38SGabriel FERNANDEZ 73344993d38SGabriel FERNANDEZ if (of_property_read_string_index(np, "clock-output-names", 73444993d38SGabriel FERNANDEZ i, &clk_name)) 73544993d38SGabriel FERNANDEZ break; 73644993d38SGabriel FERNANDEZ 73744993d38SGabriel FERNANDEZ /* 73844993d38SGabriel FERNANDEZ * If we read an empty clock name then the output is unused 73944993d38SGabriel FERNANDEZ */ 74044993d38SGabriel FERNANDEZ if (*clk_name == '\0') 74144993d38SGabriel FERNANDEZ continue; 74244993d38SGabriel FERNANDEZ 74344993d38SGabriel FERNANDEZ gate = kzalloc(sizeof(struct clk_gate), GFP_KERNEL); 74444993d38SGabriel FERNANDEZ if (!gate) 74544993d38SGabriel FERNANDEZ break; 74644993d38SGabriel FERNANDEZ 74744993d38SGabriel FERNANDEZ div = kzalloc(sizeof(struct clk_divider), GFP_KERNEL); 74844993d38SGabriel FERNANDEZ if (!div) { 74944993d38SGabriel FERNANDEZ kfree(gate); 75044993d38SGabriel FERNANDEZ break; 75144993d38SGabriel FERNANDEZ } 75244993d38SGabriel FERNANDEZ 75344993d38SGabriel FERNANDEZ mux = kzalloc(sizeof(struct clk_mux), GFP_KERNEL); 75444993d38SGabriel FERNANDEZ if (!mux) { 75544993d38SGabriel FERNANDEZ kfree(gate); 75644993d38SGabriel FERNANDEZ kfree(div); 75744993d38SGabriel FERNANDEZ break; 75844993d38SGabriel FERNANDEZ } 75944993d38SGabriel FERNANDEZ 76044993d38SGabriel FERNANDEZ gate->reg = reg + VCC_GATE_OFFSET; 76144993d38SGabriel FERNANDEZ gate->bit_idx = i; 76244993d38SGabriel FERNANDEZ gate->flags = CLK_GATE_SET_TO_DISABLE; 76344993d38SGabriel FERNANDEZ gate->lock = data->lock; 76444993d38SGabriel FERNANDEZ 76544993d38SGabriel FERNANDEZ div->reg = reg + VCC_DIV_OFFSET; 76644993d38SGabriel FERNANDEZ div->shift = 2 * i; 76744993d38SGabriel FERNANDEZ div->width = 2; 76844993d38SGabriel FERNANDEZ div->flags = CLK_DIVIDER_POWER_OF_TWO; 76944993d38SGabriel FERNANDEZ 77044993d38SGabriel FERNANDEZ mux->reg = reg + VCC_MUX_OFFSET; 77144993d38SGabriel FERNANDEZ mux->shift = 2 * i; 77244993d38SGabriel FERNANDEZ mux->mask = 0x3; 77344993d38SGabriel FERNANDEZ 77444993d38SGabriel FERNANDEZ clk = clk_register_composite(NULL, clk_name, parents, 77544993d38SGabriel FERNANDEZ num_parents, 77644993d38SGabriel FERNANDEZ &mux->hw, &clk_mux_ops, 77744993d38SGabriel FERNANDEZ &div->hw, &clk_divider_ops, 77844993d38SGabriel FERNANDEZ &gate->hw, &clk_gate_ops, 77944993d38SGabriel FERNANDEZ data->clk_flags); 78044993d38SGabriel FERNANDEZ if (IS_ERR(clk)) { 78144993d38SGabriel FERNANDEZ kfree(gate); 78244993d38SGabriel FERNANDEZ kfree(div); 78344993d38SGabriel FERNANDEZ kfree(mux); 78444993d38SGabriel FERNANDEZ goto err; 78544993d38SGabriel FERNANDEZ } 78644993d38SGabriel FERNANDEZ 78744993d38SGabriel FERNANDEZ pr_debug("%s: parent %s rate %u\n", 78844993d38SGabriel FERNANDEZ __clk_get_name(clk), 78944993d38SGabriel FERNANDEZ __clk_get_name(clk_get_parent(clk)), 79044993d38SGabriel FERNANDEZ (unsigned int)clk_get_rate(clk)); 79144993d38SGabriel FERNANDEZ 79244993d38SGabriel FERNANDEZ clk_data->clks[i] = clk; 79344993d38SGabriel FERNANDEZ } 79444993d38SGabriel FERNANDEZ 79544993d38SGabriel FERNANDEZ kfree(parents); 79644993d38SGabriel FERNANDEZ 79744993d38SGabriel FERNANDEZ of_clk_add_provider(np, of_clk_src_onecell_get, clk_data); 79844993d38SGabriel FERNANDEZ return; 79944993d38SGabriel FERNANDEZ 80044993d38SGabriel FERNANDEZ err: 80144993d38SGabriel FERNANDEZ for (i = 0; i < clk_data->clk_num; i++) { 80244993d38SGabriel FERNANDEZ struct clk_composite *composite; 80344993d38SGabriel FERNANDEZ 80444993d38SGabriel FERNANDEZ if (!clk_data->clks[i]) 80544993d38SGabriel FERNANDEZ continue; 80644993d38SGabriel FERNANDEZ 80744993d38SGabriel FERNANDEZ composite = container_of(__clk_get_hw(clk_data->clks[i]), 80844993d38SGabriel FERNANDEZ struct clk_composite, hw); 80944993d38SGabriel FERNANDEZ kfree(container_of(composite->gate_hw, struct clk_gate, hw)); 81044993d38SGabriel FERNANDEZ kfree(container_of(composite->rate_hw, struct clk_divider, hw)); 81144993d38SGabriel FERNANDEZ kfree(container_of(composite->mux_hw, struct clk_mux, hw)); 81244993d38SGabriel FERNANDEZ } 81344993d38SGabriel FERNANDEZ 81444993d38SGabriel FERNANDEZ if (clk_data) 81544993d38SGabriel FERNANDEZ kfree(clk_data->clks); 81644993d38SGabriel FERNANDEZ 81744993d38SGabriel FERNANDEZ kfree(clk_data); 81844993d38SGabriel FERNANDEZ kfree(parents); 81944993d38SGabriel FERNANDEZ } 82044993d38SGabriel FERNANDEZ CLK_OF_DECLARE(clkgen_vcc, "st,clkgen-vcc", st_of_clkgen_vcc_setup); 821