385 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (c) 2013 Samsung Electronics Co., Ltd.4 * Copyright (c) 2013 Linaro Ltd.5 * Author: Thomas Abraham <thomas.ab@samsung.com>6 *7 * This file includes utility functions to register clocks to common8 * clock framework for Samsung platforms.9*/10 11#include <linux/slab.h>12#include <linux/clkdev.h>13#include <linux/clk.h>14#include <linux/clk-provider.h>15#include <linux/io.h>16#include <linux/of_address.h>17#include <linux/syscore_ops.h>18 19#include "clk.h"20 21static LIST_HEAD(clock_reg_cache_list);22 23void samsung_clk_save(void __iomem *base,24 struct samsung_clk_reg_dump *rd,25 unsigned int num_regs)26{27 for (; num_regs > 0; --num_regs, ++rd)28 rd->value = readl(base + rd->offset);29}30 31void samsung_clk_restore(void __iomem *base,32 const struct samsung_clk_reg_dump *rd,33 unsigned int num_regs)34{35 for (; num_regs > 0; --num_regs, ++rd)36 writel(rd->value, base + rd->offset);37}38 39struct samsung_clk_reg_dump *samsung_clk_alloc_reg_dump(40 const unsigned long *rdump,41 unsigned long nr_rdump)42{43 struct samsung_clk_reg_dump *rd;44 unsigned int i;45 46 rd = kcalloc(nr_rdump, sizeof(*rd), GFP_KERNEL);47 if (!rd)48 return NULL;49 50 for (i = 0; i < nr_rdump; ++i)51 rd[i].offset = rdump[i];52 53 return rd;54}55 56/**57 * samsung_clk_init() - Create and initialize a clock provider object58 * @dev: CMU device to enable runtime PM, or NULL if RPM is not needed59 * @base: Start address (mapped) of CMU registers60 * @nr_clks: Total clock count to allocate in clock provider object61 *62 * Setup the essentials required to support clock lookup using Common Clock63 * Framework.64 *65 * Return: Allocated and initialized clock provider object.66 */67struct samsung_clk_provider * __init samsung_clk_init(struct device *dev,68 void __iomem *base, unsigned long nr_clks)69{70 struct samsung_clk_provider *ctx;71 int i;72 73 ctx = kzalloc(struct_size(ctx, clk_data.hws, nr_clks), GFP_KERNEL);74 if (!ctx)75 panic("could not allocate clock provider context.\n");76 77 for (i = 0; i < nr_clks; ++i)78 ctx->clk_data.hws[i] = ERR_PTR(-ENOENT);79 80 ctx->dev = dev;81 ctx->reg_base = base;82 ctx->clk_data.num = nr_clks;83 spin_lock_init(&ctx->lock);84 85 return ctx;86}87 88void __init samsung_clk_of_add_provider(struct device_node *np,89 struct samsung_clk_provider *ctx)90{91 if (np) {92 if (of_clk_add_hw_provider(np, of_clk_hw_onecell_get,93 &ctx->clk_data))94 panic("could not register clk provider\n");95 }96}97 98/* add a clock instance to the clock lookup table used for dt based lookup */99void samsung_clk_add_lookup(struct samsung_clk_provider *ctx,100 struct clk_hw *clk_hw, unsigned int id)101{102 if (id)103 ctx->clk_data.hws[id] = clk_hw;104}105 106/* register a list of aliases */107void __init samsung_clk_register_alias(struct samsung_clk_provider *ctx,108 const struct samsung_clock_alias *list,109 unsigned int nr_clk)110{111 struct clk_hw *clk_hw;112 unsigned int idx, ret;113 114 for (idx = 0; idx < nr_clk; idx++, list++) {115 if (!list->id) {116 pr_err("%s: clock id missing for index %d\n", __func__,117 idx);118 continue;119 }120 121 clk_hw = ctx->clk_data.hws[list->id];122 if (!clk_hw) {123 pr_err("%s: failed to find clock %d\n", __func__,124 list->id);125 continue;126 }127 128 ret = clk_hw_register_clkdev(clk_hw, list->alias,129 list->dev_name);130 if (ret)131 pr_err("%s: failed to register lookup %s\n",132 __func__, list->alias);133 }134}135 136/* register a list of fixed clocks */137void __init samsung_clk_register_fixed_rate(struct samsung_clk_provider *ctx,138 const struct samsung_fixed_rate_clock *list,139 unsigned int nr_clk)140{141 struct clk_hw *clk_hw;142 unsigned int idx;143 144 for (idx = 0; idx < nr_clk; idx++, list++) {145 clk_hw = clk_hw_register_fixed_rate(ctx->dev, list->name,146 list->parent_name, list->flags, list->fixed_rate);147 if (IS_ERR(clk_hw)) {148 pr_err("%s: failed to register clock %s\n", __func__,149 list->name);150 continue;151 }152 153 samsung_clk_add_lookup(ctx, clk_hw, list->id);154 }155}156 157/* register a list of fixed factor clocks */158void __init samsung_clk_register_fixed_factor(struct samsung_clk_provider *ctx,159 const struct samsung_fixed_factor_clock *list, unsigned int nr_clk)160{161 struct clk_hw *clk_hw;162 unsigned int idx;163 164 for (idx = 0; idx < nr_clk; idx++, list++) {165 clk_hw = clk_hw_register_fixed_factor(ctx->dev, list->name,166 list->parent_name, list->flags, list->mult, list->div);167 if (IS_ERR(clk_hw)) {168 pr_err("%s: failed to register clock %s\n", __func__,169 list->name);170 continue;171 }172 173 samsung_clk_add_lookup(ctx, clk_hw, list->id);174 }175}176 177/* register a list of mux clocks */178void __init samsung_clk_register_mux(struct samsung_clk_provider *ctx,179 const struct samsung_mux_clock *list,180 unsigned int nr_clk)181{182 struct clk_hw *clk_hw;183 unsigned int idx;184 185 for (idx = 0; idx < nr_clk; idx++, list++) {186 clk_hw = clk_hw_register_mux(ctx->dev, list->name,187 list->parent_names, list->num_parents, list->flags,188 ctx->reg_base + list->offset,189 list->shift, list->width, list->mux_flags, &ctx->lock);190 if (IS_ERR(clk_hw)) {191 pr_err("%s: failed to register clock %s\n", __func__,192 list->name);193 continue;194 }195 196 samsung_clk_add_lookup(ctx, clk_hw, list->id);197 }198}199 200/* register a list of div clocks */201void __init samsung_clk_register_div(struct samsung_clk_provider *ctx,202 const struct samsung_div_clock *list,203 unsigned int nr_clk)204{205 struct clk_hw *clk_hw;206 unsigned int idx;207 208 for (idx = 0; idx < nr_clk; idx++, list++) {209 if (list->table)210 clk_hw = clk_hw_register_divider_table(ctx->dev,211 list->name, list->parent_name, list->flags,212 ctx->reg_base + list->offset,213 list->shift, list->width, list->div_flags,214 list->table, &ctx->lock);215 else216 clk_hw = clk_hw_register_divider(ctx->dev, list->name,217 list->parent_name, list->flags,218 ctx->reg_base + list->offset, list->shift,219 list->width, list->div_flags, &ctx->lock);220 if (IS_ERR(clk_hw)) {221 pr_err("%s: failed to register clock %s\n", __func__,222 list->name);223 continue;224 }225 226 samsung_clk_add_lookup(ctx, clk_hw, list->id);227 }228}229 230/* register a list of gate clocks */231void __init samsung_clk_register_gate(struct samsung_clk_provider *ctx,232 const struct samsung_gate_clock *list,233 unsigned int nr_clk)234{235 struct clk_hw *clk_hw;236 unsigned int idx;237 238 for (idx = 0; idx < nr_clk; idx++, list++) {239 clk_hw = clk_hw_register_gate(ctx->dev, list->name, list->parent_name,240 list->flags, ctx->reg_base + list->offset,241 list->bit_idx, list->gate_flags, &ctx->lock);242 if (IS_ERR(clk_hw)) {243 pr_err("%s: failed to register clock %s\n", __func__,244 list->name);245 continue;246 }247 248 samsung_clk_add_lookup(ctx, clk_hw, list->id);249 }250}251 252/*253 * obtain the clock speed of all external fixed clock sources from device254 * tree and register it255 */256void __init samsung_clk_of_register_fixed_ext(struct samsung_clk_provider *ctx,257 struct samsung_fixed_rate_clock *fixed_rate_clk,258 unsigned int nr_fixed_rate_clk,259 const struct of_device_id *clk_matches)260{261 const struct of_device_id *match;262 struct device_node *clk_np;263 u32 freq;264 265 for_each_matching_node_and_match(clk_np, clk_matches, &match) {266 if (of_property_read_u32(clk_np, "clock-frequency", &freq))267 continue;268 fixed_rate_clk[(unsigned long)match->data].fixed_rate = freq;269 }270 samsung_clk_register_fixed_rate(ctx, fixed_rate_clk, nr_fixed_rate_clk);271}272 273#ifdef CONFIG_PM_SLEEP274static int samsung_clk_suspend(void)275{276 struct samsung_clock_reg_cache *reg_cache;277 278 list_for_each_entry(reg_cache, &clock_reg_cache_list, node) {279 samsung_clk_save(reg_cache->reg_base, reg_cache->rdump,280 reg_cache->rd_num);281 samsung_clk_restore(reg_cache->reg_base, reg_cache->rsuspend,282 reg_cache->rsuspend_num);283 }284 return 0;285}286 287static void samsung_clk_resume(void)288{289 struct samsung_clock_reg_cache *reg_cache;290 291 list_for_each_entry(reg_cache, &clock_reg_cache_list, node)292 samsung_clk_restore(reg_cache->reg_base, reg_cache->rdump,293 reg_cache->rd_num);294}295 296static struct syscore_ops samsung_clk_syscore_ops = {297 .suspend = samsung_clk_suspend,298 .resume = samsung_clk_resume,299};300 301void samsung_clk_extended_sleep_init(void __iomem *reg_base,302 const unsigned long *rdump,303 unsigned long nr_rdump,304 const struct samsung_clk_reg_dump *rsuspend,305 unsigned long nr_rsuspend)306{307 struct samsung_clock_reg_cache *reg_cache;308 309 reg_cache = kzalloc(sizeof(struct samsung_clock_reg_cache),310 GFP_KERNEL);311 if (!reg_cache)312 panic("could not allocate register reg_cache.\n");313 reg_cache->rdump = samsung_clk_alloc_reg_dump(rdump, nr_rdump);314 315 if (!reg_cache->rdump)316 panic("could not allocate register dump storage.\n");317 318 if (list_empty(&clock_reg_cache_list))319 register_syscore_ops(&samsung_clk_syscore_ops);320 321 reg_cache->reg_base = reg_base;322 reg_cache->rd_num = nr_rdump;323 reg_cache->rsuspend = rsuspend;324 reg_cache->rsuspend_num = nr_rsuspend;325 list_add_tail(®_cache->node, &clock_reg_cache_list);326}327#endif328 329/**330 * samsung_cmu_register_clocks() - Register all clocks provided in CMU object331 * @ctx: Clock provider object332 * @cmu: CMU object with clocks to register333 */334void __init samsung_cmu_register_clocks(struct samsung_clk_provider *ctx,335 const struct samsung_cmu_info *cmu)336{337 if (cmu->pll_clks)338 samsung_clk_register_pll(ctx, cmu->pll_clks, cmu->nr_pll_clks);339 if (cmu->mux_clks)340 samsung_clk_register_mux(ctx, cmu->mux_clks, cmu->nr_mux_clks);341 if (cmu->div_clks)342 samsung_clk_register_div(ctx, cmu->div_clks, cmu->nr_div_clks);343 if (cmu->gate_clks)344 samsung_clk_register_gate(ctx, cmu->gate_clks,345 cmu->nr_gate_clks);346 if (cmu->fixed_clks)347 samsung_clk_register_fixed_rate(ctx, cmu->fixed_clks,348 cmu->nr_fixed_clks);349 if (cmu->fixed_factor_clks)350 samsung_clk_register_fixed_factor(ctx, cmu->fixed_factor_clks,351 cmu->nr_fixed_factor_clks);352 if (cmu->cpu_clks)353 samsung_clk_register_cpu(ctx, cmu->cpu_clks, cmu->nr_cpu_clks);354}355 356/*357 * Common function which registers plls, muxes, dividers and gates358 * for each CMU. It also add CMU register list to register cache.359 */360struct samsung_clk_provider * __init samsung_cmu_register_one(361 struct device_node *np,362 const struct samsung_cmu_info *cmu)363{364 void __iomem *reg_base;365 struct samsung_clk_provider *ctx;366 367 reg_base = of_iomap(np, 0);368 if (!reg_base) {369 panic("%s: failed to map registers\n", __func__);370 return NULL;371 }372 373 ctx = samsung_clk_init(NULL, reg_base, cmu->nr_clk_ids);374 samsung_cmu_register_clocks(ctx, cmu);375 376 if (cmu->clk_regs)377 samsung_clk_extended_sleep_init(reg_base,378 cmu->clk_regs, cmu->nr_clk_regs,379 cmu->suspend_regs, cmu->nr_suspend_regs);380 381 samsung_clk_of_add_provider(np, ctx);382 383 return ctx;384}385