1a245fecbSHeiko Stübner /* 2a245fecbSHeiko Stübner * Copyright (c) 2014 MundoReader S.L. 3a245fecbSHeiko Stübner * Author: Heiko Stuebner <heiko@sntech.de> 4a245fecbSHeiko Stübner * 5a245fecbSHeiko Stübner * based on 6a245fecbSHeiko Stübner * 7a245fecbSHeiko Stübner * samsung/clk.c 8a245fecbSHeiko Stübner * Copyright (c) 2013 Samsung Electronics Co., Ltd. 9a245fecbSHeiko Stübner * Copyright (c) 2013 Linaro Ltd. 10a245fecbSHeiko Stübner * Author: Thomas Abraham <thomas.ab@samsung.com> 11a245fecbSHeiko Stübner * 12a245fecbSHeiko Stübner * This program is free software; you can redistribute it and/or modify 13a245fecbSHeiko Stübner * it under the terms of the GNU General Public License as published by 14a245fecbSHeiko Stübner * the Free Software Foundation; either version 2 of the License, or 15a245fecbSHeiko Stübner * (at your option) any later version. 16a245fecbSHeiko Stübner * 17a245fecbSHeiko Stübner * This program is distributed in the hope that it will be useful, 18a245fecbSHeiko Stübner * but WITHOUT ANY WARRANTY; without even the implied warranty of 19a245fecbSHeiko Stübner * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20a245fecbSHeiko Stübner * GNU General Public License for more details. 21a245fecbSHeiko Stübner */ 22a245fecbSHeiko Stübner 23a245fecbSHeiko Stübner #include <linux/slab.h> 24a245fecbSHeiko Stübner #include <linux/clk.h> 25a245fecbSHeiko Stübner #include <linux/clk-provider.h> 2690c59025SHeiko Stübner #include <linux/mfd/syscon.h> 2790c59025SHeiko Stübner #include <linux/regmap.h> 286f1294b5SHeiko Stübner #include <linux/reboot.h> 29a245fecbSHeiko Stübner #include "clk.h" 30a245fecbSHeiko Stübner 31a245fecbSHeiko Stübner /** 32a245fecbSHeiko Stübner * Register a clock branch. 33a245fecbSHeiko Stübner * Most clock branches have a form like 34a245fecbSHeiko Stübner * 35a245fecbSHeiko Stübner * src1 --|--\ 36a245fecbSHeiko Stübner * |M |--[GATE]-[DIV]- 37a245fecbSHeiko Stübner * src2 --|--/ 38a245fecbSHeiko Stübner * 39a245fecbSHeiko Stübner * sometimes without one of those components. 40a245fecbSHeiko Stübner */ 411a4b1819SHeiko Stübner static struct clk *rockchip_clk_register_branch(const char *name, 424a1caed3SUwe Kleine-König const char *const *parent_names, u8 num_parents, void __iomem *base, 43a245fecbSHeiko Stübner int muxdiv_offset, u8 mux_shift, u8 mux_width, u8 mux_flags, 44a245fecbSHeiko Stübner u8 div_shift, u8 div_width, u8 div_flags, 45a245fecbSHeiko Stübner struct clk_div_table *div_table, int gate_offset, 46a245fecbSHeiko Stübner u8 gate_shift, u8 gate_flags, unsigned long flags, 47a245fecbSHeiko Stübner spinlock_t *lock) 48a245fecbSHeiko Stübner { 49a245fecbSHeiko Stübner struct clk *clk; 50a245fecbSHeiko Stübner struct clk_mux *mux = NULL; 51a245fecbSHeiko Stübner struct clk_gate *gate = NULL; 52a245fecbSHeiko Stübner struct clk_divider *div = NULL; 53a245fecbSHeiko Stübner const struct clk_ops *mux_ops = NULL, *div_ops = NULL, 54a245fecbSHeiko Stübner *gate_ops = NULL; 55a245fecbSHeiko Stübner 56a245fecbSHeiko Stübner if (num_parents > 1) { 57a245fecbSHeiko Stübner mux = kzalloc(sizeof(*mux), GFP_KERNEL); 58a245fecbSHeiko Stübner if (!mux) 59a245fecbSHeiko Stübner return ERR_PTR(-ENOMEM); 60a245fecbSHeiko Stübner 61a245fecbSHeiko Stübner mux->reg = base + muxdiv_offset; 62a245fecbSHeiko Stübner mux->shift = mux_shift; 63a245fecbSHeiko Stübner mux->mask = BIT(mux_width) - 1; 64a245fecbSHeiko Stübner mux->flags = mux_flags; 65a245fecbSHeiko Stübner mux->lock = lock; 66a245fecbSHeiko Stübner mux_ops = (mux_flags & CLK_MUX_READ_ONLY) ? &clk_mux_ro_ops 67a245fecbSHeiko Stübner : &clk_mux_ops; 68a245fecbSHeiko Stübner } 69a245fecbSHeiko Stübner 70a245fecbSHeiko Stübner if (gate_offset >= 0) { 71a245fecbSHeiko Stübner gate = kzalloc(sizeof(*gate), GFP_KERNEL); 72a245fecbSHeiko Stübner if (!gate) 732467b674SShawn Lin goto err_gate; 74a245fecbSHeiko Stübner 75a245fecbSHeiko Stübner gate->flags = gate_flags; 76a245fecbSHeiko Stübner gate->reg = base + gate_offset; 77a245fecbSHeiko Stübner gate->bit_idx = gate_shift; 78a245fecbSHeiko Stübner gate->lock = lock; 79a245fecbSHeiko Stübner gate_ops = &clk_gate_ops; 80a245fecbSHeiko Stübner } 81a245fecbSHeiko Stübner 82a245fecbSHeiko Stübner if (div_width > 0) { 83a245fecbSHeiko Stübner div = kzalloc(sizeof(*div), GFP_KERNEL); 84a245fecbSHeiko Stübner if (!div) 852467b674SShawn Lin goto err_div; 86a245fecbSHeiko Stübner 87a245fecbSHeiko Stübner div->flags = div_flags; 88a245fecbSHeiko Stübner div->reg = base + muxdiv_offset; 89a245fecbSHeiko Stübner div->shift = div_shift; 90a245fecbSHeiko Stübner div->width = div_width; 91a245fecbSHeiko Stübner div->lock = lock; 92a245fecbSHeiko Stübner div->table = div_table; 93e6d5e7d9SJames Hogan div_ops = &clk_divider_ops; 94a245fecbSHeiko Stübner } 95a245fecbSHeiko Stübner 96a245fecbSHeiko Stübner clk = clk_register_composite(NULL, name, parent_names, num_parents, 97a245fecbSHeiko Stübner mux ? &mux->hw : NULL, mux_ops, 98a245fecbSHeiko Stübner div ? &div->hw : NULL, div_ops, 99a245fecbSHeiko Stübner gate ? &gate->hw : NULL, gate_ops, 100a245fecbSHeiko Stübner flags); 101a245fecbSHeiko Stübner 102a245fecbSHeiko Stübner return clk; 1032467b674SShawn Lin err_div: 1042467b674SShawn Lin kfree(gate); 1052467b674SShawn Lin err_gate: 1062467b674SShawn Lin kfree(mux); 1072467b674SShawn Lin return ERR_PTR(-ENOMEM); 108a245fecbSHeiko Stübner } 109a245fecbSHeiko Stübner 1108ca1ca8fSHeiko Stuebner struct rockchip_clk_frac { 1118ca1ca8fSHeiko Stuebner struct notifier_block clk_nb; 1128ca1ca8fSHeiko Stuebner struct clk_fractional_divider div; 1138ca1ca8fSHeiko Stuebner struct clk_gate gate; 1148ca1ca8fSHeiko Stuebner 1158ca1ca8fSHeiko Stuebner struct clk_mux mux; 1168ca1ca8fSHeiko Stuebner const struct clk_ops *mux_ops; 1178ca1ca8fSHeiko Stuebner int mux_frac_idx; 1188ca1ca8fSHeiko Stuebner 1198ca1ca8fSHeiko Stuebner bool rate_change_remuxed; 1208ca1ca8fSHeiko Stuebner int rate_change_idx; 1218ca1ca8fSHeiko Stuebner }; 1228ca1ca8fSHeiko Stuebner 1238ca1ca8fSHeiko Stuebner #define to_rockchip_clk_frac_nb(nb) \ 1248ca1ca8fSHeiko Stuebner container_of(nb, struct rockchip_clk_frac, clk_nb) 1258ca1ca8fSHeiko Stuebner 1268ca1ca8fSHeiko Stuebner static int rockchip_clk_frac_notifier_cb(struct notifier_block *nb, 1278ca1ca8fSHeiko Stuebner unsigned long event, void *data) 1288ca1ca8fSHeiko Stuebner { 1298ca1ca8fSHeiko Stuebner struct clk_notifier_data *ndata = data; 1308ca1ca8fSHeiko Stuebner struct rockchip_clk_frac *frac = to_rockchip_clk_frac_nb(nb); 1318ca1ca8fSHeiko Stuebner struct clk_mux *frac_mux = &frac->mux; 1328ca1ca8fSHeiko Stuebner int ret = 0; 1338ca1ca8fSHeiko Stuebner 1348ca1ca8fSHeiko Stuebner pr_debug("%s: event %lu, old_rate %lu, new_rate: %lu\n", 1358ca1ca8fSHeiko Stuebner __func__, event, ndata->old_rate, ndata->new_rate); 1368ca1ca8fSHeiko Stuebner if (event == PRE_RATE_CHANGE) { 1378ca1ca8fSHeiko Stuebner frac->rate_change_idx = frac->mux_ops->get_parent(&frac_mux->hw); 1388ca1ca8fSHeiko Stuebner if (frac->rate_change_idx != frac->mux_frac_idx) { 1398ca1ca8fSHeiko Stuebner frac->mux_ops->set_parent(&frac_mux->hw, frac->mux_frac_idx); 1408ca1ca8fSHeiko Stuebner frac->rate_change_remuxed = 1; 1418ca1ca8fSHeiko Stuebner } 1428ca1ca8fSHeiko Stuebner } else if (event == POST_RATE_CHANGE) { 1438ca1ca8fSHeiko Stuebner /* 1448ca1ca8fSHeiko Stuebner * The POST_RATE_CHANGE notifier runs directly after the 1458ca1ca8fSHeiko Stuebner * divider clock is set in clk_change_rate, so we'll have 1468ca1ca8fSHeiko Stuebner * remuxed back to the original parent before clk_change_rate 1478ca1ca8fSHeiko Stuebner * reaches the mux itself. 1488ca1ca8fSHeiko Stuebner */ 1498ca1ca8fSHeiko Stuebner if (frac->rate_change_remuxed) { 1508ca1ca8fSHeiko Stuebner frac->mux_ops->set_parent(&frac_mux->hw, frac->rate_change_idx); 1518ca1ca8fSHeiko Stuebner frac->rate_change_remuxed = 0; 1528ca1ca8fSHeiko Stuebner } 1538ca1ca8fSHeiko Stuebner } 1548ca1ca8fSHeiko Stuebner 1558ca1ca8fSHeiko Stuebner return notifier_from_errno(ret); 1568ca1ca8fSHeiko Stuebner } 1578ca1ca8fSHeiko Stuebner 158b2155a71SHeiko Stübner static struct clk *rockchip_clk_register_frac_branch(const char *name, 1594a1caed3SUwe Kleine-König const char *const *parent_names, u8 num_parents, 1604a1caed3SUwe Kleine-König void __iomem *base, int muxdiv_offset, u8 div_flags, 161b2155a71SHeiko Stübner int gate_offset, u8 gate_shift, u8 gate_flags, 1628ca1ca8fSHeiko Stuebner unsigned long flags, struct rockchip_clk_branch *child, 1638ca1ca8fSHeiko Stuebner spinlock_t *lock) 164b2155a71SHeiko Stübner { 1658ca1ca8fSHeiko Stuebner struct rockchip_clk_frac *frac; 166b2155a71SHeiko Stübner struct clk *clk; 167b2155a71SHeiko Stübner struct clk_gate *gate = NULL; 168b2155a71SHeiko Stübner struct clk_fractional_divider *div = NULL; 169b2155a71SHeiko Stübner const struct clk_ops *div_ops = NULL, *gate_ops = NULL; 170b2155a71SHeiko Stübner 1718ca1ca8fSHeiko Stuebner if (muxdiv_offset < 0) 1728ca1ca8fSHeiko Stuebner return ERR_PTR(-EINVAL); 1738ca1ca8fSHeiko Stuebner 1748ca1ca8fSHeiko Stuebner if (child && child->branch_type != branch_mux) { 1758ca1ca8fSHeiko Stuebner pr_err("%s: fractional child clock for %s can only be a mux\n", 1768ca1ca8fSHeiko Stuebner __func__, name); 1778ca1ca8fSHeiko Stuebner return ERR_PTR(-EINVAL); 1788ca1ca8fSHeiko Stuebner } 1798ca1ca8fSHeiko Stuebner 1808ca1ca8fSHeiko Stuebner frac = kzalloc(sizeof(*frac), GFP_KERNEL); 1818ca1ca8fSHeiko Stuebner if (!frac) 182b2155a71SHeiko Stübner return ERR_PTR(-ENOMEM); 183b2155a71SHeiko Stübner 1848ca1ca8fSHeiko Stuebner if (gate_offset >= 0) { 1858ca1ca8fSHeiko Stuebner gate = &frac->gate; 186b2155a71SHeiko Stübner gate->flags = gate_flags; 187b2155a71SHeiko Stübner gate->reg = base + gate_offset; 188b2155a71SHeiko Stübner gate->bit_idx = gate_shift; 189b2155a71SHeiko Stübner gate->lock = lock; 190b2155a71SHeiko Stübner gate_ops = &clk_gate_ops; 191b2155a71SHeiko Stübner } 192b2155a71SHeiko Stübner 1938ca1ca8fSHeiko Stuebner div = &frac->div; 194b2155a71SHeiko Stübner div->flags = div_flags; 195b2155a71SHeiko Stübner div->reg = base + muxdiv_offset; 196b2155a71SHeiko Stübner div->mshift = 16; 1975d49a6e1SAndy Shevchenko div->mwidth = 16; 1985d49a6e1SAndy Shevchenko div->mmask = GENMASK(div->mwidth - 1, 0) << div->mshift; 199b2155a71SHeiko Stübner div->nshift = 0; 2005d49a6e1SAndy Shevchenko div->nwidth = 16; 2015d49a6e1SAndy Shevchenko div->nmask = GENMASK(div->nwidth - 1, 0) << div->nshift; 202b2155a71SHeiko Stübner div->lock = lock; 203b2155a71SHeiko Stübner div_ops = &clk_fractional_divider_ops; 204b2155a71SHeiko Stübner 205b2155a71SHeiko Stübner clk = clk_register_composite(NULL, name, parent_names, num_parents, 206b2155a71SHeiko Stübner NULL, NULL, 207b2155a71SHeiko Stübner &div->hw, div_ops, 208b2155a71SHeiko Stübner gate ? &gate->hw : NULL, gate_ops, 2098ca1ca8fSHeiko Stuebner flags | CLK_SET_RATE_UNGATE); 2108ca1ca8fSHeiko Stuebner if (IS_ERR(clk)) { 2118ca1ca8fSHeiko Stuebner kfree(frac); 2128ca1ca8fSHeiko Stuebner return clk; 2138ca1ca8fSHeiko Stuebner } 2148ca1ca8fSHeiko Stuebner 2158ca1ca8fSHeiko Stuebner if (child) { 2168ca1ca8fSHeiko Stuebner struct clk_mux *frac_mux = &frac->mux; 2178ca1ca8fSHeiko Stuebner struct clk_init_data init; 2188ca1ca8fSHeiko Stuebner struct clk *mux_clk; 2198ca1ca8fSHeiko Stuebner int i, ret; 2208ca1ca8fSHeiko Stuebner 2218ca1ca8fSHeiko Stuebner frac->mux_frac_idx = -1; 2228ca1ca8fSHeiko Stuebner for (i = 0; i < child->num_parents; i++) { 2238ca1ca8fSHeiko Stuebner if (!strcmp(name, child->parent_names[i])) { 2248ca1ca8fSHeiko Stuebner pr_debug("%s: found fractional parent in mux at pos %d\n", 2258ca1ca8fSHeiko Stuebner __func__, i); 2268ca1ca8fSHeiko Stuebner frac->mux_frac_idx = i; 2278ca1ca8fSHeiko Stuebner break; 2288ca1ca8fSHeiko Stuebner } 2298ca1ca8fSHeiko Stuebner } 2308ca1ca8fSHeiko Stuebner 2318ca1ca8fSHeiko Stuebner frac->mux_ops = &clk_mux_ops; 2328ca1ca8fSHeiko Stuebner frac->clk_nb.notifier_call = rockchip_clk_frac_notifier_cb; 2338ca1ca8fSHeiko Stuebner 2348ca1ca8fSHeiko Stuebner frac_mux->reg = base + child->muxdiv_offset; 2358ca1ca8fSHeiko Stuebner frac_mux->shift = child->mux_shift; 2368ca1ca8fSHeiko Stuebner frac_mux->mask = BIT(child->mux_width) - 1; 2378ca1ca8fSHeiko Stuebner frac_mux->flags = child->mux_flags; 2388ca1ca8fSHeiko Stuebner frac_mux->lock = lock; 2398ca1ca8fSHeiko Stuebner frac_mux->hw.init = &init; 2408ca1ca8fSHeiko Stuebner 2418ca1ca8fSHeiko Stuebner init.name = child->name; 2428ca1ca8fSHeiko Stuebner init.flags = child->flags | CLK_SET_RATE_PARENT; 2438ca1ca8fSHeiko Stuebner init.ops = frac->mux_ops; 2448ca1ca8fSHeiko Stuebner init.parent_names = child->parent_names; 2458ca1ca8fSHeiko Stuebner init.num_parents = child->num_parents; 2468ca1ca8fSHeiko Stuebner 2478ca1ca8fSHeiko Stuebner mux_clk = clk_register(NULL, &frac_mux->hw); 2488ca1ca8fSHeiko Stuebner if (IS_ERR(mux_clk)) 2498ca1ca8fSHeiko Stuebner return clk; 2508ca1ca8fSHeiko Stuebner 2518ca1ca8fSHeiko Stuebner rockchip_clk_add_lookup(mux_clk, child->id); 2528ca1ca8fSHeiko Stuebner 2538ca1ca8fSHeiko Stuebner /* notifier on the fraction divider to catch rate changes */ 2548ca1ca8fSHeiko Stuebner if (frac->mux_frac_idx >= 0) { 2558ca1ca8fSHeiko Stuebner ret = clk_notifier_register(clk, &frac->clk_nb); 2568ca1ca8fSHeiko Stuebner if (ret) 2578ca1ca8fSHeiko Stuebner pr_err("%s: failed to register clock notifier for %s\n", 2588ca1ca8fSHeiko Stuebner __func__, name); 2598ca1ca8fSHeiko Stuebner } else { 2608ca1ca8fSHeiko Stuebner pr_warn("%s: could not find %s as parent of %s, rate changes may not work\n", 2618ca1ca8fSHeiko Stuebner __func__, name, child->name); 2628ca1ca8fSHeiko Stuebner } 2638ca1ca8fSHeiko Stuebner } 264b2155a71SHeiko Stübner 265b2155a71SHeiko Stübner return clk; 266b2155a71SHeiko Stübner } 267b2155a71SHeiko Stübner 268*29a30c26SHeiko Stuebner static struct clk *rockchip_clk_register_factor_branch(const char *name, 269*29a30c26SHeiko Stuebner const char *const *parent_names, u8 num_parents, 270*29a30c26SHeiko Stuebner void __iomem *base, unsigned int mult, unsigned int div, 271*29a30c26SHeiko Stuebner int gate_offset, u8 gate_shift, u8 gate_flags, 272*29a30c26SHeiko Stuebner unsigned long flags, spinlock_t *lock) 273*29a30c26SHeiko Stuebner { 274*29a30c26SHeiko Stuebner struct clk *clk; 275*29a30c26SHeiko Stuebner struct clk_gate *gate = NULL; 276*29a30c26SHeiko Stuebner struct clk_fixed_factor *fix = NULL; 277*29a30c26SHeiko Stuebner 278*29a30c26SHeiko Stuebner /* without gate, register a simple factor clock */ 279*29a30c26SHeiko Stuebner if (gate_offset == 0) { 280*29a30c26SHeiko Stuebner return clk_register_fixed_factor(NULL, name, 281*29a30c26SHeiko Stuebner parent_names[0], flags, mult, 282*29a30c26SHeiko Stuebner div); 283*29a30c26SHeiko Stuebner } 284*29a30c26SHeiko Stuebner 285*29a30c26SHeiko Stuebner gate = kzalloc(sizeof(*gate), GFP_KERNEL); 286*29a30c26SHeiko Stuebner if (!gate) 287*29a30c26SHeiko Stuebner return ERR_PTR(-ENOMEM); 288*29a30c26SHeiko Stuebner 289*29a30c26SHeiko Stuebner gate->flags = gate_flags; 290*29a30c26SHeiko Stuebner gate->reg = base + gate_offset; 291*29a30c26SHeiko Stuebner gate->bit_idx = gate_shift; 292*29a30c26SHeiko Stuebner gate->lock = lock; 293*29a30c26SHeiko Stuebner 294*29a30c26SHeiko Stuebner fix = kzalloc(sizeof(*fix), GFP_KERNEL); 295*29a30c26SHeiko Stuebner if (!fix) { 296*29a30c26SHeiko Stuebner kfree(gate); 297*29a30c26SHeiko Stuebner return ERR_PTR(-ENOMEM); 298*29a30c26SHeiko Stuebner } 299*29a30c26SHeiko Stuebner 300*29a30c26SHeiko Stuebner fix->mult = mult; 301*29a30c26SHeiko Stuebner fix->div = div; 302*29a30c26SHeiko Stuebner 303*29a30c26SHeiko Stuebner clk = clk_register_composite(NULL, name, parent_names, num_parents, 304*29a30c26SHeiko Stuebner NULL, NULL, 305*29a30c26SHeiko Stuebner &fix->hw, &clk_fixed_factor_ops, 306*29a30c26SHeiko Stuebner &gate->hw, &clk_gate_ops, flags); 307*29a30c26SHeiko Stuebner if (IS_ERR(clk)) { 308*29a30c26SHeiko Stuebner kfree(fix); 309*29a30c26SHeiko Stuebner kfree(gate); 310*29a30c26SHeiko Stuebner } 311*29a30c26SHeiko Stuebner 312*29a30c26SHeiko Stuebner return clk; 313*29a30c26SHeiko Stuebner } 314*29a30c26SHeiko Stuebner 315a245fecbSHeiko Stübner static DEFINE_SPINLOCK(clk_lock); 316a245fecbSHeiko Stübner static struct clk **clk_table; 317a245fecbSHeiko Stübner static void __iomem *reg_base; 318a245fecbSHeiko Stübner static struct clk_onecell_data clk_data; 31990c59025SHeiko Stübner static struct device_node *cru_node; 32090c59025SHeiko Stübner static struct regmap *grf; 321a245fecbSHeiko Stübner 322a245fecbSHeiko Stübner void __init rockchip_clk_init(struct device_node *np, void __iomem *base, 323a245fecbSHeiko Stübner unsigned long nr_clks) 324a245fecbSHeiko Stübner { 325a245fecbSHeiko Stübner reg_base = base; 32690c59025SHeiko Stübner cru_node = np; 32790c59025SHeiko Stübner grf = ERR_PTR(-EPROBE_DEFER); 328a245fecbSHeiko Stübner 329a245fecbSHeiko Stübner clk_table = kcalloc(nr_clks, sizeof(struct clk *), GFP_KERNEL); 330a245fecbSHeiko Stübner if (!clk_table) 331a245fecbSHeiko Stübner pr_err("%s: could not allocate clock lookup table\n", __func__); 332a245fecbSHeiko Stübner 333a245fecbSHeiko Stübner clk_data.clks = clk_table; 334a245fecbSHeiko Stübner clk_data.clk_num = nr_clks; 335a245fecbSHeiko Stübner of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); 336a245fecbSHeiko Stübner } 337a245fecbSHeiko Stübner 33890c59025SHeiko Stübner struct regmap *rockchip_clk_get_grf(void) 33990c59025SHeiko Stübner { 34090c59025SHeiko Stübner if (IS_ERR(grf)) 34190c59025SHeiko Stübner grf = syscon_regmap_lookup_by_phandle(cru_node, "rockchip,grf"); 34290c59025SHeiko Stübner return grf; 34390c59025SHeiko Stübner } 34490c59025SHeiko Stübner 345a245fecbSHeiko Stübner void rockchip_clk_add_lookup(struct clk *clk, unsigned int id) 346a245fecbSHeiko Stübner { 347a245fecbSHeiko Stübner if (clk_table && id) 348a245fecbSHeiko Stübner clk_table[id] = clk; 349a245fecbSHeiko Stübner } 350a245fecbSHeiko Stübner 35190c59025SHeiko Stübner void __init rockchip_clk_register_plls(struct rockchip_pll_clock *list, 35290c59025SHeiko Stübner unsigned int nr_pll, int grf_lock_offset) 35390c59025SHeiko Stübner { 35490c59025SHeiko Stübner struct clk *clk; 35590c59025SHeiko Stübner int idx; 35690c59025SHeiko Stübner 35790c59025SHeiko Stübner for (idx = 0; idx < nr_pll; idx++, list++) { 35890c59025SHeiko Stübner clk = rockchip_clk_register_pll(list->type, list->name, 35990c59025SHeiko Stübner list->parent_names, list->num_parents, 36090c59025SHeiko Stübner reg_base, list->con_offset, grf_lock_offset, 36190c59025SHeiko Stübner list->lock_shift, list->mode_offset, 3624f8a7c54SHeiko Stuebner list->mode_shift, list->rate_table, 3634f8a7c54SHeiko Stuebner list->pll_flags, &clk_lock); 36490c59025SHeiko Stübner if (IS_ERR(clk)) { 36590c59025SHeiko Stübner pr_err("%s: failed to register clock %s\n", __func__, 36690c59025SHeiko Stübner list->name); 36790c59025SHeiko Stübner continue; 36890c59025SHeiko Stübner } 36990c59025SHeiko Stübner 37090c59025SHeiko Stübner rockchip_clk_add_lookup(clk, list->id); 37190c59025SHeiko Stübner } 37290c59025SHeiko Stübner } 37390c59025SHeiko Stübner 374a245fecbSHeiko Stübner void __init rockchip_clk_register_branches( 375a245fecbSHeiko Stübner struct rockchip_clk_branch *list, 376a245fecbSHeiko Stübner unsigned int nr_clk) 377a245fecbSHeiko Stübner { 378a245fecbSHeiko Stübner struct clk *clk = NULL; 379a245fecbSHeiko Stübner unsigned int idx; 380a245fecbSHeiko Stübner unsigned long flags; 381a245fecbSHeiko Stübner 382a245fecbSHeiko Stübner for (idx = 0; idx < nr_clk; idx++, list++) { 383a245fecbSHeiko Stübner flags = list->flags; 384a245fecbSHeiko Stübner 385a245fecbSHeiko Stübner /* catch simple muxes */ 386a245fecbSHeiko Stübner switch (list->branch_type) { 387a245fecbSHeiko Stübner case branch_mux: 388a245fecbSHeiko Stübner clk = clk_register_mux(NULL, list->name, 389a245fecbSHeiko Stübner list->parent_names, list->num_parents, 390a245fecbSHeiko Stübner flags, reg_base + list->muxdiv_offset, 391a245fecbSHeiko Stübner list->mux_shift, list->mux_width, 392a245fecbSHeiko Stübner list->mux_flags, &clk_lock); 393a245fecbSHeiko Stübner break; 394a245fecbSHeiko Stübner case branch_divider: 395a245fecbSHeiko Stübner if (list->div_table) 396a245fecbSHeiko Stübner clk = clk_register_divider_table(NULL, 397a245fecbSHeiko Stübner list->name, list->parent_names[0], 398a245fecbSHeiko Stübner flags, reg_base + list->muxdiv_offset, 399a245fecbSHeiko Stübner list->div_shift, list->div_width, 400a245fecbSHeiko Stübner list->div_flags, list->div_table, 401a245fecbSHeiko Stübner &clk_lock); 402a245fecbSHeiko Stübner else 403a245fecbSHeiko Stübner clk = clk_register_divider(NULL, list->name, 404a245fecbSHeiko Stübner list->parent_names[0], flags, 405a245fecbSHeiko Stübner reg_base + list->muxdiv_offset, 406a245fecbSHeiko Stübner list->div_shift, list->div_width, 407a245fecbSHeiko Stübner list->div_flags, &clk_lock); 408a245fecbSHeiko Stübner break; 409a245fecbSHeiko Stübner case branch_fraction_divider: 410b2155a71SHeiko Stübner clk = rockchip_clk_register_frac_branch(list->name, 411b2155a71SHeiko Stübner list->parent_names, list->num_parents, 412b2155a71SHeiko Stübner reg_base, list->muxdiv_offset, list->div_flags, 413b2155a71SHeiko Stübner list->gate_offset, list->gate_shift, 4148ca1ca8fSHeiko Stuebner list->gate_flags, flags, list->child, 4158ca1ca8fSHeiko Stuebner &clk_lock); 416a245fecbSHeiko Stübner break; 417a245fecbSHeiko Stübner case branch_gate: 418a245fecbSHeiko Stübner flags |= CLK_SET_RATE_PARENT; 419a245fecbSHeiko Stübner 420a245fecbSHeiko Stübner clk = clk_register_gate(NULL, list->name, 421a245fecbSHeiko Stübner list->parent_names[0], flags, 422a245fecbSHeiko Stübner reg_base + list->gate_offset, 423a245fecbSHeiko Stübner list->gate_shift, list->gate_flags, &clk_lock); 424a245fecbSHeiko Stübner break; 425a245fecbSHeiko Stübner case branch_composite: 426a245fecbSHeiko Stübner clk = rockchip_clk_register_branch(list->name, 427a245fecbSHeiko Stübner list->parent_names, list->num_parents, 428a245fecbSHeiko Stübner reg_base, list->muxdiv_offset, list->mux_shift, 429a245fecbSHeiko Stübner list->mux_width, list->mux_flags, 430a245fecbSHeiko Stübner list->div_shift, list->div_width, 431a245fecbSHeiko Stübner list->div_flags, list->div_table, 432a245fecbSHeiko Stübner list->gate_offset, list->gate_shift, 433a245fecbSHeiko Stübner list->gate_flags, flags, &clk_lock); 434a245fecbSHeiko Stübner break; 43589bf26cbSAlexandru M Stan case branch_mmc: 43689bf26cbSAlexandru M Stan clk = rockchip_clk_register_mmc( 43789bf26cbSAlexandru M Stan list->name, 43889bf26cbSAlexandru M Stan list->parent_names, list->num_parents, 43989bf26cbSAlexandru M Stan reg_base + list->muxdiv_offset, 44089bf26cbSAlexandru M Stan list->div_shift 44189bf26cbSAlexandru M Stan ); 44289bf26cbSAlexandru M Stan break; 4438a76f443SHeiko Stuebner case branch_inverter: 4448a76f443SHeiko Stuebner clk = rockchip_clk_register_inverter( 4458a76f443SHeiko Stuebner list->name, list->parent_names, 4468a76f443SHeiko Stuebner list->num_parents, 4478a76f443SHeiko Stuebner reg_base + list->muxdiv_offset, 4488a76f443SHeiko Stuebner list->div_shift, list->div_flags, &clk_lock); 4498a76f443SHeiko Stuebner break; 450*29a30c26SHeiko Stuebner case branch_factor: 451*29a30c26SHeiko Stuebner clk = rockchip_clk_register_factor_branch( 452*29a30c26SHeiko Stuebner list->name, list->parent_names, 453*29a30c26SHeiko Stuebner list->num_parents, reg_base, 454*29a30c26SHeiko Stuebner list->div_shift, list->div_width, 455*29a30c26SHeiko Stuebner list->gate_offset, list->gate_shift, 456*29a30c26SHeiko Stuebner list->gate_flags, flags, &clk_lock); 457*29a30c26SHeiko Stuebner break; 458a245fecbSHeiko Stübner } 459a245fecbSHeiko Stübner 460a245fecbSHeiko Stübner /* none of the cases above matched */ 461a245fecbSHeiko Stübner if (!clk) { 462a245fecbSHeiko Stübner pr_err("%s: unknown clock type %d\n", 463a245fecbSHeiko Stübner __func__, list->branch_type); 464a245fecbSHeiko Stübner continue; 465a245fecbSHeiko Stübner } 466a245fecbSHeiko Stübner 467a245fecbSHeiko Stübner if (IS_ERR(clk)) { 468a245fecbSHeiko Stübner pr_err("%s: failed to register clock %s: %ld\n", 469a245fecbSHeiko Stübner __func__, list->name, PTR_ERR(clk)); 470a245fecbSHeiko Stübner continue; 471a245fecbSHeiko Stübner } 472a245fecbSHeiko Stübner 473a245fecbSHeiko Stübner rockchip_clk_add_lookup(clk, list->id); 474a245fecbSHeiko Stübner } 475a245fecbSHeiko Stübner } 476fe94f974SHeiko Stübner 477f6fba5f6SHeiko Stuebner void __init rockchip_clk_register_armclk(unsigned int lookup_id, 4784a1caed3SUwe Kleine-König const char *name, const char *const *parent_names, 479f6fba5f6SHeiko Stuebner u8 num_parents, 480f6fba5f6SHeiko Stuebner const struct rockchip_cpuclk_reg_data *reg_data, 481f6fba5f6SHeiko Stuebner const struct rockchip_cpuclk_rate_table *rates, 482f6fba5f6SHeiko Stuebner int nrates) 483f6fba5f6SHeiko Stuebner { 484f6fba5f6SHeiko Stuebner struct clk *clk; 485f6fba5f6SHeiko Stuebner 486f6fba5f6SHeiko Stuebner clk = rockchip_clk_register_cpuclk(name, parent_names, num_parents, 487f6fba5f6SHeiko Stuebner reg_data, rates, nrates, reg_base, 488f6fba5f6SHeiko Stuebner &clk_lock); 489f6fba5f6SHeiko Stuebner if (IS_ERR(clk)) { 490f6fba5f6SHeiko Stuebner pr_err("%s: failed to register clock %s: %ld\n", 491f6fba5f6SHeiko Stuebner __func__, name, PTR_ERR(clk)); 492f6fba5f6SHeiko Stuebner return; 493f6fba5f6SHeiko Stuebner } 494f6fba5f6SHeiko Stuebner 495f6fba5f6SHeiko Stuebner rockchip_clk_add_lookup(clk, lookup_id); 496f6fba5f6SHeiko Stuebner } 497f6fba5f6SHeiko Stuebner 498692d8328SUwe Kleine-König void __init rockchip_clk_protect_critical(const char *const clocks[], 499692d8328SUwe Kleine-König int nclocks) 500fe94f974SHeiko Stübner { 501fe94f974SHeiko Stübner int i; 502fe94f974SHeiko Stübner 503fe94f974SHeiko Stübner /* Protect the clocks that needs to stay on */ 504fe94f974SHeiko Stübner for (i = 0; i < nclocks; i++) { 505fe94f974SHeiko Stübner struct clk *clk = __clk_lookup(clocks[i]); 506fe94f974SHeiko Stübner 507fe94f974SHeiko Stübner if (clk) 508fe94f974SHeiko Stübner clk_prepare_enable(clk); 509fe94f974SHeiko Stübner } 510fe94f974SHeiko Stübner } 5116f1294b5SHeiko Stübner 5126f1294b5SHeiko Stübner static unsigned int reg_restart; 513dfff24bdSHeiko Stuebner static void (*cb_restart)(void); 5146f1294b5SHeiko Stübner static int rockchip_restart_notify(struct notifier_block *this, 5156f1294b5SHeiko Stübner unsigned long mode, void *cmd) 5166f1294b5SHeiko Stübner { 517dfff24bdSHeiko Stuebner if (cb_restart) 518dfff24bdSHeiko Stuebner cb_restart(); 519dfff24bdSHeiko Stuebner 5206f1294b5SHeiko Stübner writel(0xfdb9, reg_base + reg_restart); 5216f1294b5SHeiko Stübner return NOTIFY_DONE; 5226f1294b5SHeiko Stübner } 5236f1294b5SHeiko Stübner 5246f1294b5SHeiko Stübner static struct notifier_block rockchip_restart_handler = { 5256f1294b5SHeiko Stübner .notifier_call = rockchip_restart_notify, 5266f1294b5SHeiko Stübner .priority = 128, 5276f1294b5SHeiko Stübner }; 5286f1294b5SHeiko Stübner 529dfff24bdSHeiko Stuebner void __init rockchip_register_restart_notifier(unsigned int reg, void (*cb)(void)) 5306f1294b5SHeiko Stübner { 5316f1294b5SHeiko Stübner int ret; 5326f1294b5SHeiko Stübner 5336f1294b5SHeiko Stübner reg_restart = reg; 534dfff24bdSHeiko Stuebner cb_restart = cb; 5356f1294b5SHeiko Stübner ret = register_restart_handler(&rockchip_restart_handler); 5366f1294b5SHeiko Stübner if (ret) 5376f1294b5SHeiko Stübner pr_err("%s: cannot register restart handler, %d\n", 5386f1294b5SHeiko Stübner __func__, ret); 5396f1294b5SHeiko Stübner } 540