Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Copyright (c) 2013 Samsung Electronics Co., Ltd.
0004  * Copyright (c) 2013 Linaro Ltd.
0005  * Author: Thomas Abraham <thomas.ab@samsung.com>
0006  *
0007  * This file includes utility functions to register clocks to common
0008  * clock framework for Samsung platforms.
0009 */
0010 
0011 #include <linux/slab.h>
0012 #include <linux/clkdev.h>
0013 #include <linux/clk.h>
0014 #include <linux/clk-provider.h>
0015 #include <linux/io.h>
0016 #include <linux/of_address.h>
0017 #include <linux/syscore_ops.h>
0018 
0019 #include "clk.h"
0020 
0021 static LIST_HEAD(clock_reg_cache_list);
0022 
0023 void samsung_clk_save(void __iomem *base,
0024                     struct samsung_clk_reg_dump *rd,
0025                     unsigned int num_regs)
0026 {
0027     for (; num_regs > 0; --num_regs, ++rd)
0028         rd->value = readl(base + rd->offset);
0029 }
0030 
0031 void samsung_clk_restore(void __iomem *base,
0032                       const struct samsung_clk_reg_dump *rd,
0033                       unsigned int num_regs)
0034 {
0035     for (; num_regs > 0; --num_regs, ++rd)
0036         writel(rd->value, base + rd->offset);
0037 }
0038 
0039 struct samsung_clk_reg_dump *samsung_clk_alloc_reg_dump(
0040                         const unsigned long *rdump,
0041                         unsigned long nr_rdump)
0042 {
0043     struct samsung_clk_reg_dump *rd;
0044     unsigned int i;
0045 
0046     rd = kcalloc(nr_rdump, sizeof(*rd), GFP_KERNEL);
0047     if (!rd)
0048         return NULL;
0049 
0050     for (i = 0; i < nr_rdump; ++i)
0051         rd[i].offset = rdump[i];
0052 
0053     return rd;
0054 }
0055 
0056 /* setup the essentials required to support clock lookup using ccf */
0057 struct samsung_clk_provider *__init samsung_clk_init(struct device_node *np,
0058             void __iomem *base, unsigned long nr_clks)
0059 {
0060     struct samsung_clk_provider *ctx;
0061     int i;
0062 
0063     ctx = kzalloc(struct_size(ctx, clk_data.hws, nr_clks), GFP_KERNEL);
0064     if (!ctx)
0065         panic("could not allocate clock provider context.\n");
0066 
0067     for (i = 0; i < nr_clks; ++i)
0068         ctx->clk_data.hws[i] = ERR_PTR(-ENOENT);
0069 
0070     ctx->reg_base = base;
0071     ctx->clk_data.num = nr_clks;
0072     spin_lock_init(&ctx->lock);
0073 
0074     return ctx;
0075 }
0076 
0077 void __init samsung_clk_of_add_provider(struct device_node *np,
0078                 struct samsung_clk_provider *ctx)
0079 {
0080     if (np) {
0081         if (of_clk_add_hw_provider(np, of_clk_hw_onecell_get,
0082                     &ctx->clk_data))
0083             panic("could not register clk provider\n");
0084     }
0085 }
0086 
0087 /* add a clock instance to the clock lookup table used for dt based lookup */
0088 void samsung_clk_add_lookup(struct samsung_clk_provider *ctx,
0089                 struct clk_hw *clk_hw, unsigned int id)
0090 {
0091     if (id)
0092         ctx->clk_data.hws[id] = clk_hw;
0093 }
0094 
0095 /* register a list of aliases */
0096 void __init samsung_clk_register_alias(struct samsung_clk_provider *ctx,
0097                 const struct samsung_clock_alias *list,
0098                 unsigned int nr_clk)
0099 {
0100     struct clk_hw *clk_hw;
0101     unsigned int idx, ret;
0102 
0103     for (idx = 0; idx < nr_clk; idx++, list++) {
0104         if (!list->id) {
0105             pr_err("%s: clock id missing for index %d\n", __func__,
0106                 idx);
0107             continue;
0108         }
0109 
0110         clk_hw = ctx->clk_data.hws[list->id];
0111         if (!clk_hw) {
0112             pr_err("%s: failed to find clock %d\n", __func__,
0113                 list->id);
0114             continue;
0115         }
0116 
0117         ret = clk_hw_register_clkdev(clk_hw, list->alias,
0118                          list->dev_name);
0119         if (ret)
0120             pr_err("%s: failed to register lookup %s\n",
0121                     __func__, list->alias);
0122     }
0123 }
0124 
0125 /* register a list of fixed clocks */
0126 void __init samsung_clk_register_fixed_rate(struct samsung_clk_provider *ctx,
0127         const struct samsung_fixed_rate_clock *list,
0128         unsigned int nr_clk)
0129 {
0130     struct clk_hw *clk_hw;
0131     unsigned int idx, ret;
0132 
0133     for (idx = 0; idx < nr_clk; idx++, list++) {
0134         clk_hw = clk_hw_register_fixed_rate(ctx->dev, list->name,
0135             list->parent_name, list->flags, list->fixed_rate);
0136         if (IS_ERR(clk_hw)) {
0137             pr_err("%s: failed to register clock %s\n", __func__,
0138                 list->name);
0139             continue;
0140         }
0141 
0142         samsung_clk_add_lookup(ctx, clk_hw, list->id);
0143 
0144         /*
0145          * Unconditionally add a clock lookup for the fixed rate clocks.
0146          * There are not many of these on any of Samsung platforms.
0147          */
0148         ret = clk_hw_register_clkdev(clk_hw, list->name, NULL);
0149         if (ret)
0150             pr_err("%s: failed to register clock lookup for %s",
0151                 __func__, list->name);
0152     }
0153 }
0154 
0155 /* register a list of fixed factor clocks */
0156 void __init samsung_clk_register_fixed_factor(struct samsung_clk_provider *ctx,
0157         const struct samsung_fixed_factor_clock *list, unsigned int nr_clk)
0158 {
0159     struct clk_hw *clk_hw;
0160     unsigned int idx;
0161 
0162     for (idx = 0; idx < nr_clk; idx++, list++) {
0163         clk_hw = clk_hw_register_fixed_factor(ctx->dev, list->name,
0164             list->parent_name, list->flags, list->mult, list->div);
0165         if (IS_ERR(clk_hw)) {
0166             pr_err("%s: failed to register clock %s\n", __func__,
0167                 list->name);
0168             continue;
0169         }
0170 
0171         samsung_clk_add_lookup(ctx, clk_hw, list->id);
0172     }
0173 }
0174 
0175 /* register a list of mux clocks */
0176 void __init samsung_clk_register_mux(struct samsung_clk_provider *ctx,
0177                 const struct samsung_mux_clock *list,
0178                 unsigned int nr_clk)
0179 {
0180     struct clk_hw *clk_hw;
0181     unsigned int idx;
0182 
0183     for (idx = 0; idx < nr_clk; idx++, list++) {
0184         clk_hw = clk_hw_register_mux(ctx->dev, list->name,
0185             list->parent_names, list->num_parents, list->flags,
0186             ctx->reg_base + list->offset,
0187             list->shift, list->width, list->mux_flags, &ctx->lock);
0188         if (IS_ERR(clk_hw)) {
0189             pr_err("%s: failed to register clock %s\n", __func__,
0190                 list->name);
0191             continue;
0192         }
0193 
0194         samsung_clk_add_lookup(ctx, clk_hw, list->id);
0195     }
0196 }
0197 
0198 /* register a list of div clocks */
0199 void __init samsung_clk_register_div(struct samsung_clk_provider *ctx,
0200                 const struct samsung_div_clock *list,
0201                 unsigned int nr_clk)
0202 {
0203     struct clk_hw *clk_hw;
0204     unsigned int idx;
0205 
0206     for (idx = 0; idx < nr_clk; idx++, list++) {
0207         if (list->table)
0208             clk_hw = clk_hw_register_divider_table(ctx->dev,
0209                 list->name, list->parent_name, list->flags,
0210                 ctx->reg_base + list->offset,
0211                 list->shift, list->width, list->div_flags,
0212                 list->table, &ctx->lock);
0213         else
0214             clk_hw = clk_hw_register_divider(ctx->dev, list->name,
0215                 list->parent_name, list->flags,
0216                 ctx->reg_base + list->offset, list->shift,
0217                 list->width, list->div_flags, &ctx->lock);
0218         if (IS_ERR(clk_hw)) {
0219             pr_err("%s: failed to register clock %s\n", __func__,
0220                 list->name);
0221             continue;
0222         }
0223 
0224         samsung_clk_add_lookup(ctx, clk_hw, list->id);
0225     }
0226 }
0227 
0228 /* register a list of gate clocks */
0229 void __init samsung_clk_register_gate(struct samsung_clk_provider *ctx,
0230                 const struct samsung_gate_clock *list,
0231                 unsigned int nr_clk)
0232 {
0233     struct clk_hw *clk_hw;
0234     unsigned int idx;
0235 
0236     for (idx = 0; idx < nr_clk; idx++, list++) {
0237         clk_hw = clk_hw_register_gate(ctx->dev, list->name, list->parent_name,
0238                 list->flags, ctx->reg_base + list->offset,
0239                 list->bit_idx, list->gate_flags, &ctx->lock);
0240         if (IS_ERR(clk_hw)) {
0241             pr_err("%s: failed to register clock %s\n", __func__,
0242                 list->name);
0243             continue;
0244         }
0245 
0246         samsung_clk_add_lookup(ctx, clk_hw, list->id);
0247     }
0248 }
0249 
0250 /*
0251  * obtain the clock speed of all external fixed clock sources from device
0252  * tree and register it
0253  */
0254 void __init samsung_clk_of_register_fixed_ext(struct samsung_clk_provider *ctx,
0255             struct samsung_fixed_rate_clock *fixed_rate_clk,
0256             unsigned int nr_fixed_rate_clk,
0257             const struct of_device_id *clk_matches)
0258 {
0259     const struct of_device_id *match;
0260     struct device_node *clk_np;
0261     u32 freq;
0262 
0263     for_each_matching_node_and_match(clk_np, clk_matches, &match) {
0264         if (of_property_read_u32(clk_np, "clock-frequency", &freq))
0265             continue;
0266         fixed_rate_clk[(unsigned long)match->data].fixed_rate = freq;
0267     }
0268     samsung_clk_register_fixed_rate(ctx, fixed_rate_clk, nr_fixed_rate_clk);
0269 }
0270 
0271 #ifdef CONFIG_PM_SLEEP
0272 static int samsung_clk_suspend(void)
0273 {
0274     struct samsung_clock_reg_cache *reg_cache;
0275 
0276     list_for_each_entry(reg_cache, &clock_reg_cache_list, node) {
0277         samsung_clk_save(reg_cache->reg_base, reg_cache->rdump,
0278                 reg_cache->rd_num);
0279         samsung_clk_restore(reg_cache->reg_base, reg_cache->rsuspend,
0280                 reg_cache->rsuspend_num);
0281     }
0282     return 0;
0283 }
0284 
0285 static void samsung_clk_resume(void)
0286 {
0287     struct samsung_clock_reg_cache *reg_cache;
0288 
0289     list_for_each_entry(reg_cache, &clock_reg_cache_list, node)
0290         samsung_clk_restore(reg_cache->reg_base, reg_cache->rdump,
0291                 reg_cache->rd_num);
0292 }
0293 
0294 static struct syscore_ops samsung_clk_syscore_ops = {
0295     .suspend = samsung_clk_suspend,
0296     .resume = samsung_clk_resume,
0297 };
0298 
0299 void samsung_clk_extended_sleep_init(void __iomem *reg_base,
0300             const unsigned long *rdump,
0301             unsigned long nr_rdump,
0302             const struct samsung_clk_reg_dump *rsuspend,
0303             unsigned long nr_rsuspend)
0304 {
0305     struct samsung_clock_reg_cache *reg_cache;
0306 
0307     reg_cache = kzalloc(sizeof(struct samsung_clock_reg_cache),
0308             GFP_KERNEL);
0309     if (!reg_cache)
0310         panic("could not allocate register reg_cache.\n");
0311     reg_cache->rdump = samsung_clk_alloc_reg_dump(rdump, nr_rdump);
0312 
0313     if (!reg_cache->rdump)
0314         panic("could not allocate register dump storage.\n");
0315 
0316     if (list_empty(&clock_reg_cache_list))
0317         register_syscore_ops(&samsung_clk_syscore_ops);
0318 
0319     reg_cache->reg_base = reg_base;
0320     reg_cache->rd_num = nr_rdump;
0321     reg_cache->rsuspend = rsuspend;
0322     reg_cache->rsuspend_num = nr_rsuspend;
0323     list_add_tail(&reg_cache->node, &clock_reg_cache_list);
0324 }
0325 #endif
0326 
0327 /*
0328  * Common function which registers plls, muxes, dividers and gates
0329  * for each CMU. It also add CMU register list to register cache.
0330  */
0331 struct samsung_clk_provider * __init samsung_cmu_register_one(
0332             struct device_node *np,
0333             const struct samsung_cmu_info *cmu)
0334 {
0335     void __iomem *reg_base;
0336     struct samsung_clk_provider *ctx;
0337 
0338     reg_base = of_iomap(np, 0);
0339     if (!reg_base) {
0340         panic("%s: failed to map registers\n", __func__);
0341         return NULL;
0342     }
0343 
0344     ctx = samsung_clk_init(np, reg_base, cmu->nr_clk_ids);
0345 
0346     if (cmu->pll_clks)
0347         samsung_clk_register_pll(ctx, cmu->pll_clks, cmu->nr_pll_clks,
0348             reg_base);
0349     if (cmu->mux_clks)
0350         samsung_clk_register_mux(ctx, cmu->mux_clks,
0351             cmu->nr_mux_clks);
0352     if (cmu->div_clks)
0353         samsung_clk_register_div(ctx, cmu->div_clks, cmu->nr_div_clks);
0354     if (cmu->gate_clks)
0355         samsung_clk_register_gate(ctx, cmu->gate_clks,
0356             cmu->nr_gate_clks);
0357     if (cmu->fixed_clks)
0358         samsung_clk_register_fixed_rate(ctx, cmu->fixed_clks,
0359             cmu->nr_fixed_clks);
0360     if (cmu->fixed_factor_clks)
0361         samsung_clk_register_fixed_factor(ctx, cmu->fixed_factor_clks,
0362             cmu->nr_fixed_factor_clks);
0363     if (cmu->clk_regs)
0364         samsung_clk_extended_sleep_init(reg_base,
0365             cmu->clk_regs, cmu->nr_clk_regs,
0366             cmu->suspend_regs, cmu->nr_suspend_regs);
0367     if (cmu->cpu_clks)
0368         samsung_clk_register_cpu(ctx, cmu->cpu_clks, cmu->nr_cpu_clks);
0369 
0370     samsung_clk_of_add_provider(np, ctx);
0371 
0372     return ctx;
0373 }