linux/drivers/clk/samsung/clk-cpu.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Copyright (c) 2014 Samsung Electronics Co., Ltd.
   4 * Author: Thomas Abraham <thomas.ab@samsung.com>
   5 *
   6 * Copyright (c) 2015 Samsung Electronics Co., Ltd.
   7 * Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>
   8 *
   9 * This file contains the utility function to register CPU clock for Samsung
  10 * Exynos platforms. A CPU clock is defined as a clock supplied to a CPU or a
  11 * group of CPUs. The CPU clock is typically derived from a hierarchy of clock
  12 * blocks which includes mux and divider blocks. There are a number of other
  13 * auxiliary clocks supplied to the CPU domain such as the debug blocks and AXI
  14 * clock for CPU domain. The rates of these auxiliary clocks are related to the
  15 * CPU clock rate and this relation is usually specified in the hardware manual
  16 * of the SoC or supplied after the SoC characterization.
  17 *
  18 * The below implementation of the CPU clock allows the rate changes of the CPU
  19 * clock and the corresponding rate changes of the auxillary clocks of the CPU
  20 * domain. The platform clock driver provides a clock register configuration
  21 * for each configurable rate which is then used to program the clock hardware
  22 * registers to acheive a fast co-oridinated rate change for all the CPU domain
  23 * clocks.
  24 *
  25 * On a rate change request for the CPU clock, the rate change is propagated
  26 * upto the PLL supplying the clock to the CPU domain clock blocks. While the
  27 * CPU domain PLL is reconfigured, the CPU domain clocks are driven using an
  28 * alternate clock source. If required, the alternate clock source is divided
  29 * down in order to keep the output clock rate within the previous OPP limits.
  30*/
  31
  32#include <linux/errno.h>
  33#include <linux/io.h>
  34#include <linux/slab.h>
  35#include <linux/clk.h>
  36#include <linux/clk-provider.h>
  37#include "clk-cpu.h"
  38
  39#define E4210_SRC_CPU           0x0
  40#define E4210_STAT_CPU          0x200
  41#define E4210_DIV_CPU0          0x300
  42#define E4210_DIV_CPU1          0x304
  43#define E4210_DIV_STAT_CPU0     0x400
  44#define E4210_DIV_STAT_CPU1     0x404
  45
  46#define E5433_MUX_SEL2          0x008
  47#define E5433_MUX_STAT2         0x208
  48#define E5433_DIV_CPU0          0x400
  49#define E5433_DIV_CPU1          0x404
  50#define E5433_DIV_STAT_CPU0     0x500
  51#define E5433_DIV_STAT_CPU1     0x504
  52
  53#define E4210_DIV0_RATIO0_MASK  0x7
  54#define E4210_DIV1_HPM_MASK     (0x7 << 4)
  55#define E4210_DIV1_COPY_MASK    (0x7 << 0)
  56#define E4210_MUX_HPM_MASK      (1 << 20)
  57#define E4210_DIV0_ATB_SHIFT    16
  58#define E4210_DIV0_ATB_MASK     (DIV_MASK << E4210_DIV0_ATB_SHIFT)
  59
  60#define MAX_DIV                 8
  61#define DIV_MASK                7
  62#define DIV_MASK_ALL            0xffffffff
  63#define MUX_MASK                7
  64
  65/*
  66 * Helper function to wait until divider(s) have stabilized after the divider
  67 * value has changed.
  68 */
  69static void wait_until_divider_stable(void __iomem *div_reg, unsigned long mask)
  70{
  71        unsigned long timeout = jiffies + msecs_to_jiffies(10);
  72
  73        do {
  74                if (!(readl(div_reg) & mask))
  75                        return;
  76        } while (time_before(jiffies, timeout));
  77
  78        if (!(readl(div_reg) & mask))
  79                return;
  80
  81        pr_err("%s: timeout in divider stablization\n", __func__);
  82}
  83
  84/*
  85 * Helper function to wait until mux has stabilized after the mux selection
  86 * value was changed.
  87 */
  88static void wait_until_mux_stable(void __iomem *mux_reg, u32 mux_pos,
  89                                        unsigned long mux_value)
  90{
  91        unsigned long timeout = jiffies + msecs_to_jiffies(10);
  92
  93        do {
  94                if (((readl(mux_reg) >> mux_pos) & MUX_MASK) == mux_value)
  95                        return;
  96        } while (time_before(jiffies, timeout));
  97
  98        if (((readl(mux_reg) >> mux_pos) & MUX_MASK) == mux_value)
  99                return;
 100
 101        pr_err("%s: re-parenting mux timed-out\n", __func__);
 102}
 103
 104/* common round rate callback useable for all types of CPU clocks */
 105static long exynos_cpuclk_round_rate(struct clk_hw *hw,
 106                        unsigned long drate, unsigned long *prate)
 107{
 108        struct clk_hw *parent = clk_hw_get_parent(hw);
 109        *prate = clk_hw_round_rate(parent, drate);
 110        return *prate;
 111}
 112
 113/* common recalc rate callback useable for all types of CPU clocks */
 114static unsigned long exynos_cpuclk_recalc_rate(struct clk_hw *hw,
 115                        unsigned long parent_rate)
 116{
 117        /*
 118         * The CPU clock output (armclk) rate is the same as its parent
 119         * rate. Although there exist certain dividers inside the CPU
 120         * clock block that could be used to divide the parent clock,
 121         * the driver does not make use of them currently, except during
 122         * frequency transitions.
 123         */
 124        return parent_rate;
 125}
 126
 127static const struct clk_ops exynos_cpuclk_clk_ops = {
 128        .recalc_rate = exynos_cpuclk_recalc_rate,
 129        .round_rate = exynos_cpuclk_round_rate,
 130};
 131
 132/*
 133 * Helper function to set the 'safe' dividers for the CPU clock. The parameters
 134 * div and mask contain the divider value and the register bit mask of the
 135 * dividers to be programmed.
 136 */
 137static void exynos_set_safe_div(void __iomem *base, unsigned long div,
 138                                        unsigned long mask)
 139{
 140        unsigned long div0;
 141
 142        div0 = readl(base + E4210_DIV_CPU0);
 143        div0 = (div0 & ~mask) | (div & mask);
 144        writel(div0, base + E4210_DIV_CPU0);
 145        wait_until_divider_stable(base + E4210_DIV_STAT_CPU0, mask);
 146}
 147
 148/* handler for pre-rate change notification from parent clock */
 149static int exynos_cpuclk_pre_rate_change(struct clk_notifier_data *ndata,
 150                        struct exynos_cpuclk *cpuclk, void __iomem *base)
 151{
 152        const struct exynos_cpuclk_cfg_data *cfg_data = cpuclk->cfg;
 153        unsigned long alt_prate = clk_hw_get_rate(cpuclk->alt_parent);
 154        unsigned long alt_div = 0, alt_div_mask = DIV_MASK;
 155        unsigned long div0, div1 = 0, mux_reg;
 156        unsigned long flags;
 157
 158        /* find out the divider values to use for clock data */
 159        while ((cfg_data->prate * 1000) != ndata->new_rate) {
 160                if (cfg_data->prate == 0)
 161                        return -EINVAL;
 162                cfg_data++;
 163        }
 164
 165        spin_lock_irqsave(cpuclk->lock, flags);
 166
 167        /*
 168         * For the selected PLL clock frequency, get the pre-defined divider
 169         * values. If the clock for sclk_hpm is not sourced from apll, then
 170         * the values for DIV_COPY and DIV_HPM dividers need not be set.
 171         */
 172        div0 = cfg_data->div0;
 173        if (cpuclk->flags & CLK_CPU_HAS_DIV1) {
 174                div1 = cfg_data->div1;
 175                if (readl(base + E4210_SRC_CPU) & E4210_MUX_HPM_MASK)
 176                        div1 = readl(base + E4210_DIV_CPU1) &
 177                                (E4210_DIV1_HPM_MASK | E4210_DIV1_COPY_MASK);
 178        }
 179
 180        /*
 181         * If the old parent clock speed is less than the clock speed of
 182         * the alternate parent, then it should be ensured that at no point
 183         * the armclk speed is more than the old_prate until the dividers are
 184         * set.  Also workaround the issue of the dividers being set to lower
 185         * values before the parent clock speed is set to new lower speed
 186         * (this can result in too high speed of armclk output clocks).
 187         */
 188        if (alt_prate > ndata->old_rate || ndata->old_rate > ndata->new_rate) {
 189                unsigned long tmp_rate = min(ndata->old_rate, ndata->new_rate);
 190
 191                alt_div = DIV_ROUND_UP(alt_prate, tmp_rate) - 1;
 192                WARN_ON(alt_div >= MAX_DIV);
 193
 194                if (cpuclk->flags & CLK_CPU_NEEDS_DEBUG_ALT_DIV) {
 195                        /*
 196                         * In Exynos4210, ATB clock parent is also mout_core. So
 197                         * ATB clock also needs to be mantained at safe speed.
 198                         */
 199                        alt_div |= E4210_DIV0_ATB_MASK;
 200                        alt_div_mask |= E4210_DIV0_ATB_MASK;
 201                }
 202                exynos_set_safe_div(base, alt_div, alt_div_mask);
 203                div0 |= alt_div;
 204        }
 205
 206        /* select sclk_mpll as the alternate parent */
 207        mux_reg = readl(base + E4210_SRC_CPU);
 208        writel(mux_reg | (1 << 16), base + E4210_SRC_CPU);
 209        wait_until_mux_stable(base + E4210_STAT_CPU, 16, 2);
 210
 211        /* alternate parent is active now. set the dividers */
 212        writel(div0, base + E4210_DIV_CPU0);
 213        wait_until_divider_stable(base + E4210_DIV_STAT_CPU0, DIV_MASK_ALL);
 214
 215        if (cpuclk->flags & CLK_CPU_HAS_DIV1) {
 216                writel(div1, base + E4210_DIV_CPU1);
 217                wait_until_divider_stable(base + E4210_DIV_STAT_CPU1,
 218                                DIV_MASK_ALL);
 219        }
 220
 221        spin_unlock_irqrestore(cpuclk->lock, flags);
 222        return 0;
 223}
 224
 225/* handler for post-rate change notification from parent clock */
 226static int exynos_cpuclk_post_rate_change(struct clk_notifier_data *ndata,
 227                        struct exynos_cpuclk *cpuclk, void __iomem *base)
 228{
 229        const struct exynos_cpuclk_cfg_data *cfg_data = cpuclk->cfg;
 230        unsigned long div = 0, div_mask = DIV_MASK;
 231        unsigned long mux_reg;
 232        unsigned long flags;
 233
 234        /* find out the divider values to use for clock data */
 235        if (cpuclk->flags & CLK_CPU_NEEDS_DEBUG_ALT_DIV) {
 236                while ((cfg_data->prate * 1000) != ndata->new_rate) {
 237                        if (cfg_data->prate == 0)
 238                                return -EINVAL;
 239                        cfg_data++;
 240                }
 241        }
 242
 243        spin_lock_irqsave(cpuclk->lock, flags);
 244
 245        /* select mout_apll as the alternate parent */
 246        mux_reg = readl(base + E4210_SRC_CPU);
 247        writel(mux_reg & ~(1 << 16), base + E4210_SRC_CPU);
 248        wait_until_mux_stable(base + E4210_STAT_CPU, 16, 1);
 249
 250        if (cpuclk->flags & CLK_CPU_NEEDS_DEBUG_ALT_DIV) {
 251                div |= (cfg_data->div0 & E4210_DIV0_ATB_MASK);
 252                div_mask |= E4210_DIV0_ATB_MASK;
 253        }
 254
 255        exynos_set_safe_div(base, div, div_mask);
 256        spin_unlock_irqrestore(cpuclk->lock, flags);
 257        return 0;
 258}
 259
 260/*
 261 * Helper function to set the 'safe' dividers for the CPU clock. The parameters
 262 * div and mask contain the divider value and the register bit mask of the
 263 * dividers to be programmed.
 264 */
 265static void exynos5433_set_safe_div(void __iomem *base, unsigned long div,
 266                                        unsigned long mask)
 267{
 268        unsigned long div0;
 269
 270        div0 = readl(base + E5433_DIV_CPU0);
 271        div0 = (div0 & ~mask) | (div & mask);
 272        writel(div0, base + E5433_DIV_CPU0);
 273        wait_until_divider_stable(base + E5433_DIV_STAT_CPU0, mask);
 274}
 275
 276/* handler for pre-rate change notification from parent clock */
 277static int exynos5433_cpuclk_pre_rate_change(struct clk_notifier_data *ndata,
 278                        struct exynos_cpuclk *cpuclk, void __iomem *base)
 279{
 280        const struct exynos_cpuclk_cfg_data *cfg_data = cpuclk->cfg;
 281        unsigned long alt_prate = clk_hw_get_rate(cpuclk->alt_parent);
 282        unsigned long alt_div = 0, alt_div_mask = DIV_MASK;
 283        unsigned long div0, div1 = 0, mux_reg;
 284        unsigned long flags;
 285
 286        /* find out the divider values to use for clock data */
 287        while ((cfg_data->prate * 1000) != ndata->new_rate) {
 288                if (cfg_data->prate == 0)
 289                        return -EINVAL;
 290                cfg_data++;
 291        }
 292
 293        spin_lock_irqsave(cpuclk->lock, flags);
 294
 295        /*
 296         * For the selected PLL clock frequency, get the pre-defined divider
 297         * values.
 298         */
 299        div0 = cfg_data->div0;
 300        div1 = cfg_data->div1;
 301
 302        /*
 303         * If the old parent clock speed is less than the clock speed of
 304         * the alternate parent, then it should be ensured that at no point
 305         * the armclk speed is more than the old_prate until the dividers are
 306         * set.  Also workaround the issue of the dividers being set to lower
 307         * values before the parent clock speed is set to new lower speed
 308         * (this can result in too high speed of armclk output clocks).
 309         */
 310        if (alt_prate > ndata->old_rate || ndata->old_rate > ndata->new_rate) {
 311                unsigned long tmp_rate = min(ndata->old_rate, ndata->new_rate);
 312
 313                alt_div = DIV_ROUND_UP(alt_prate, tmp_rate) - 1;
 314                WARN_ON(alt_div >= MAX_DIV);
 315
 316                exynos5433_set_safe_div(base, alt_div, alt_div_mask);
 317                div0 |= alt_div;
 318        }
 319
 320        /* select the alternate parent */
 321        mux_reg = readl(base + E5433_MUX_SEL2);
 322        writel(mux_reg | 1, base + E5433_MUX_SEL2);
 323        wait_until_mux_stable(base + E5433_MUX_STAT2, 0, 2);
 324
 325        /* alternate parent is active now. set the dividers */
 326        writel(div0, base + E5433_DIV_CPU0);
 327        wait_until_divider_stable(base + E5433_DIV_STAT_CPU0, DIV_MASK_ALL);
 328
 329        writel(div1, base + E5433_DIV_CPU1);
 330        wait_until_divider_stable(base + E5433_DIV_STAT_CPU1, DIV_MASK_ALL);
 331
 332        spin_unlock_irqrestore(cpuclk->lock, flags);
 333        return 0;
 334}
 335
 336/* handler for post-rate change notification from parent clock */
 337static int exynos5433_cpuclk_post_rate_change(struct clk_notifier_data *ndata,
 338                        struct exynos_cpuclk *cpuclk, void __iomem *base)
 339{
 340        unsigned long div = 0, div_mask = DIV_MASK;
 341        unsigned long mux_reg;
 342        unsigned long flags;
 343
 344        spin_lock_irqsave(cpuclk->lock, flags);
 345
 346        /* select apll as the alternate parent */
 347        mux_reg = readl(base + E5433_MUX_SEL2);
 348        writel(mux_reg & ~1, base + E5433_MUX_SEL2);
 349        wait_until_mux_stable(base + E5433_MUX_STAT2, 0, 1);
 350
 351        exynos5433_set_safe_div(base, div, div_mask);
 352        spin_unlock_irqrestore(cpuclk->lock, flags);
 353        return 0;
 354}
 355
 356/*
 357 * This notifier function is called for the pre-rate and post-rate change
 358 * notifications of the parent clock of cpuclk.
 359 */
 360static int exynos_cpuclk_notifier_cb(struct notifier_block *nb,
 361                                unsigned long event, void *data)
 362{
 363        struct clk_notifier_data *ndata = data;
 364        struct exynos_cpuclk *cpuclk;
 365        void __iomem *base;
 366        int err = 0;
 367
 368        cpuclk = container_of(nb, struct exynos_cpuclk, clk_nb);
 369        base = cpuclk->ctrl_base;
 370
 371        if (event == PRE_RATE_CHANGE)
 372                err = exynos_cpuclk_pre_rate_change(ndata, cpuclk, base);
 373        else if (event == POST_RATE_CHANGE)
 374                err = exynos_cpuclk_post_rate_change(ndata, cpuclk, base);
 375
 376        return notifier_from_errno(err);
 377}
 378
 379/*
 380 * This notifier function is called for the pre-rate and post-rate change
 381 * notifications of the parent clock of cpuclk.
 382 */
 383static int exynos5433_cpuclk_notifier_cb(struct notifier_block *nb,
 384                                unsigned long event, void *data)
 385{
 386        struct clk_notifier_data *ndata = data;
 387        struct exynos_cpuclk *cpuclk;
 388        void __iomem *base;
 389        int err = 0;
 390
 391        cpuclk = container_of(nb, struct exynos_cpuclk, clk_nb);
 392        base = cpuclk->ctrl_base;
 393
 394        if (event == PRE_RATE_CHANGE)
 395                err = exynos5433_cpuclk_pre_rate_change(ndata, cpuclk, base);
 396        else if (event == POST_RATE_CHANGE)
 397                err = exynos5433_cpuclk_post_rate_change(ndata, cpuclk, base);
 398
 399        return notifier_from_errno(err);
 400}
 401
 402/* helper function to register a CPU clock */
 403int __init exynos_register_cpu_clock(struct samsung_clk_provider *ctx,
 404                unsigned int lookup_id, const char *name,
 405                const struct clk_hw *parent, const struct clk_hw *alt_parent,
 406                unsigned long offset, const struct exynos_cpuclk_cfg_data *cfg,
 407                unsigned long num_cfgs, unsigned long flags)
 408{
 409        struct exynos_cpuclk *cpuclk;
 410        struct clk_init_data init;
 411        const char *parent_name;
 412        int ret = 0;
 413
 414        if (IS_ERR(parent) || IS_ERR(alt_parent)) {
 415                pr_err("%s: invalid parent clock(s)\n", __func__);
 416                return -EINVAL;
 417        }
 418
 419        cpuclk = kzalloc(sizeof(*cpuclk), GFP_KERNEL);
 420        if (!cpuclk)
 421                return -ENOMEM;
 422
 423        parent_name = clk_hw_get_name(parent);
 424
 425        init.name = name;
 426        init.flags = CLK_SET_RATE_PARENT;
 427        init.parent_names = &parent_name;
 428        init.num_parents = 1;
 429        init.ops = &exynos_cpuclk_clk_ops;
 430
 431        cpuclk->alt_parent = alt_parent;
 432        cpuclk->hw.init = &init;
 433        cpuclk->ctrl_base = ctx->reg_base + offset;
 434        cpuclk->lock = &ctx->lock;
 435        cpuclk->flags = flags;
 436        if (flags & CLK_CPU_HAS_E5433_REGS_LAYOUT)
 437                cpuclk->clk_nb.notifier_call = exynos5433_cpuclk_notifier_cb;
 438        else
 439                cpuclk->clk_nb.notifier_call = exynos_cpuclk_notifier_cb;
 440
 441
 442        ret = clk_notifier_register(parent->clk, &cpuclk->clk_nb);
 443        if (ret) {
 444                pr_err("%s: failed to register clock notifier for %s\n",
 445                                __func__, name);
 446                goto free_cpuclk;
 447        }
 448
 449        cpuclk->cfg = kmemdup(cfg, sizeof(*cfg) * num_cfgs, GFP_KERNEL);
 450        if (!cpuclk->cfg) {
 451                ret = -ENOMEM;
 452                goto unregister_clk_nb;
 453        }
 454
 455        ret = clk_hw_register(NULL, &cpuclk->hw);
 456        if (ret) {
 457                pr_err("%s: could not register cpuclk %s\n", __func__,  name);
 458                goto free_cpuclk_data;
 459        }
 460
 461        samsung_clk_add_lookup(ctx, &cpuclk->hw, lookup_id);
 462        return 0;
 463
 464free_cpuclk_data:
 465        kfree(cpuclk->cfg);
 466unregister_clk_nb:
 467        clk_notifier_unregister(parent->clk, &cpuclk->clk_nb);
 468free_cpuclk:
 469        kfree(cpuclk);
 470        return ret;
 471}
 472