linux/drivers/clk/sunxi/clk-sun9i-core.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * Copyright 2014 Chen-Yu Tsai
   4 *
   5 * Chen-Yu Tsai <wens@csie.org>
   6 */
   7
   8#include <linux/clk.h>
   9#include <linux/clk-provider.h>
  10#include <linux/of.h>
  11#include <linux/of_address.h>
  12#include <linux/log2.h>
  13
  14#include "clk-factors.h"
  15
  16
  17/*
  18 * sun9i_a80_get_pll4_factors() - calculates n, p, m factors for PLL4
  19 * PLL4 rate is calculated as follows
  20 * rate = (parent_rate * n >> p) / (m + 1);
  21 * parent_rate is always 24MHz
  22 *
  23 * p and m are named div1 and div2 in Allwinner's SDK
  24 */
  25
  26static void sun9i_a80_get_pll4_factors(struct factors_request *req)
  27{
  28        int n;
  29        int m = 1;
  30        int p = 1;
  31
  32        /* Normalize value to a 6 MHz multiple (24 MHz / 4) */
  33        n = DIV_ROUND_UP(req->rate, 6000000);
  34
  35        /* If n is too large switch to steps of 12 MHz */
  36        if (n > 255) {
  37                m = 0;
  38                n = (n + 1) / 2;
  39        }
  40
  41        /* If n is still too large switch to steps of 24 MHz */
  42        if (n > 255) {
  43                p = 0;
  44                n = (n + 1) / 2;
  45        }
  46
  47        /* n must be between 12 and 255 */
  48        if (n > 255)
  49                n = 255;
  50        else if (n < 12)
  51                n = 12;
  52
  53        req->rate = ((24000000 * n) >> p) / (m + 1);
  54        req->n = n;
  55        req->m = m;
  56        req->p = p;
  57}
  58
  59static const struct clk_factors_config sun9i_a80_pll4_config = {
  60        .mshift = 18,
  61        .mwidth = 1,
  62        .nshift = 8,
  63        .nwidth = 8,
  64        .pshift = 16,
  65        .pwidth = 1,
  66};
  67
  68static const struct factors_data sun9i_a80_pll4_data __initconst = {
  69        .enable = 31,
  70        .table = &sun9i_a80_pll4_config,
  71        .getter = sun9i_a80_get_pll4_factors,
  72};
  73
  74static DEFINE_SPINLOCK(sun9i_a80_pll4_lock);
  75
  76static void __init sun9i_a80_pll4_setup(struct device_node *node)
  77{
  78        void __iomem *reg;
  79
  80        reg = of_io_request_and_map(node, 0, of_node_full_name(node));
  81        if (IS_ERR(reg)) {
  82                pr_err("Could not get registers for a80-pll4-clk: %pOFn\n",
  83                       node);
  84                return;
  85        }
  86
  87        sunxi_factors_register(node, &sun9i_a80_pll4_data,
  88                               &sun9i_a80_pll4_lock, reg);
  89}
  90CLK_OF_DECLARE(sun9i_a80_pll4, "allwinner,sun9i-a80-pll4-clk", sun9i_a80_pll4_setup);
  91
  92
  93/*
  94 * sun9i_a80_get_gt_factors() - calculates m factor for GT
  95 * GT rate is calculated as follows
  96 * rate = parent_rate / (m + 1);
  97 */
  98
  99static void sun9i_a80_get_gt_factors(struct factors_request *req)
 100{
 101        u32 div;
 102
 103        if (req->parent_rate < req->rate)
 104                req->rate = req->parent_rate;
 105
 106        div = DIV_ROUND_UP(req->parent_rate, req->rate);
 107
 108        /* maximum divider is 4 */
 109        if (div > 4)
 110                div = 4;
 111
 112        req->rate = req->parent_rate / div;
 113        req->m = div;
 114}
 115
 116static const struct clk_factors_config sun9i_a80_gt_config = {
 117        .mshift = 0,
 118        .mwidth = 2,
 119};
 120
 121static const struct factors_data sun9i_a80_gt_data __initconst = {
 122        .mux = 24,
 123        .muxmask = BIT(1) | BIT(0),
 124        .table = &sun9i_a80_gt_config,
 125        .getter = sun9i_a80_get_gt_factors,
 126};
 127
 128static DEFINE_SPINLOCK(sun9i_a80_gt_lock);
 129
 130static void __init sun9i_a80_gt_setup(struct device_node *node)
 131{
 132        void __iomem *reg;
 133
 134        reg = of_io_request_and_map(node, 0, of_node_full_name(node));
 135        if (IS_ERR(reg)) {
 136                pr_err("Could not get registers for a80-gt-clk: %pOFn\n",
 137                       node);
 138                return;
 139        }
 140
 141        /* The GT bus clock needs to be always enabled */
 142        sunxi_factors_register_critical(node, &sun9i_a80_gt_data,
 143                                        &sun9i_a80_gt_lock, reg);
 144}
 145CLK_OF_DECLARE(sun9i_a80_gt, "allwinner,sun9i-a80-gt-clk", sun9i_a80_gt_setup);
 146
 147
 148/*
 149 * sun9i_a80_get_ahb_factors() - calculates p factor for AHB0/1/2
 150 * AHB rate is calculated as follows
 151 * rate = parent_rate >> p;
 152 */
 153
 154static void sun9i_a80_get_ahb_factors(struct factors_request *req)
 155{
 156        u32 _p;
 157
 158        if (req->parent_rate < req->rate)
 159                req->rate = req->parent_rate;
 160
 161        _p = order_base_2(DIV_ROUND_UP(req->parent_rate, req->rate));
 162
 163        /* maximum p is 3 */
 164        if (_p > 3)
 165                _p = 3;
 166
 167        req->rate = req->parent_rate >> _p;
 168        req->p = _p;
 169}
 170
 171static const struct clk_factors_config sun9i_a80_ahb_config = {
 172        .pshift = 0,
 173        .pwidth = 2,
 174};
 175
 176static const struct factors_data sun9i_a80_ahb_data __initconst = {
 177        .mux = 24,
 178        .muxmask = BIT(1) | BIT(0),
 179        .table = &sun9i_a80_ahb_config,
 180        .getter = sun9i_a80_get_ahb_factors,
 181};
 182
 183static DEFINE_SPINLOCK(sun9i_a80_ahb_lock);
 184
 185static void __init sun9i_a80_ahb_setup(struct device_node *node)
 186{
 187        void __iomem *reg;
 188
 189        reg = of_io_request_and_map(node, 0, of_node_full_name(node));
 190        if (IS_ERR(reg)) {
 191                pr_err("Could not get registers for a80-ahb-clk: %pOFn\n",
 192                       node);
 193                return;
 194        }
 195
 196        sunxi_factors_register(node, &sun9i_a80_ahb_data,
 197                               &sun9i_a80_ahb_lock, reg);
 198}
 199CLK_OF_DECLARE(sun9i_a80_ahb, "allwinner,sun9i-a80-ahb-clk", sun9i_a80_ahb_setup);
 200
 201
 202static const struct factors_data sun9i_a80_apb0_data __initconst = {
 203        .mux = 24,
 204        .muxmask = BIT(0),
 205        .table = &sun9i_a80_ahb_config,
 206        .getter = sun9i_a80_get_ahb_factors,
 207};
 208
 209static DEFINE_SPINLOCK(sun9i_a80_apb0_lock);
 210
 211static void __init sun9i_a80_apb0_setup(struct device_node *node)
 212{
 213        void __iomem *reg;
 214
 215        reg = of_io_request_and_map(node, 0, of_node_full_name(node));
 216        if (IS_ERR(reg)) {
 217                pr_err("Could not get registers for a80-apb0-clk: %pOFn\n",
 218                       node);
 219                return;
 220        }
 221
 222        sunxi_factors_register(node, &sun9i_a80_apb0_data,
 223                               &sun9i_a80_apb0_lock, reg);
 224}
 225CLK_OF_DECLARE(sun9i_a80_apb0, "allwinner,sun9i-a80-apb0-clk", sun9i_a80_apb0_setup);
 226
 227
 228/*
 229 * sun9i_a80_get_apb1_factors() - calculates m, p factors for APB1
 230 * APB1 rate is calculated as follows
 231 * rate = (parent_rate >> p) / (m + 1);
 232 */
 233
 234static void sun9i_a80_get_apb1_factors(struct factors_request *req)
 235{
 236        u32 div;
 237
 238        if (req->parent_rate < req->rate)
 239                req->rate = req->parent_rate;
 240
 241        div = DIV_ROUND_UP(req->parent_rate, req->rate);
 242
 243        /* Highest possible divider is 256 (p = 3, m = 31) */
 244        if (div > 256)
 245                div = 256;
 246
 247        req->p = order_base_2(div);
 248        req->m = (req->parent_rate >> req->p) - 1;
 249        req->rate = (req->parent_rate >> req->p) / (req->m + 1);
 250}
 251
 252static const struct clk_factors_config sun9i_a80_apb1_config = {
 253        .mshift = 0,
 254        .mwidth = 5,
 255        .pshift = 16,
 256        .pwidth = 2,
 257};
 258
 259static const struct factors_data sun9i_a80_apb1_data __initconst = {
 260        .mux = 24,
 261        .muxmask = BIT(0),
 262        .table = &sun9i_a80_apb1_config,
 263        .getter = sun9i_a80_get_apb1_factors,
 264};
 265
 266static DEFINE_SPINLOCK(sun9i_a80_apb1_lock);
 267
 268static void __init sun9i_a80_apb1_setup(struct device_node *node)
 269{
 270        void __iomem *reg;
 271
 272        reg = of_io_request_and_map(node, 0, of_node_full_name(node));
 273        if (IS_ERR(reg)) {
 274                pr_err("Could not get registers for a80-apb1-clk: %pOFn\n",
 275                       node);
 276                return;
 277        }
 278
 279        sunxi_factors_register(node, &sun9i_a80_apb1_data,
 280                               &sun9i_a80_apb1_lock, reg);
 281}
 282CLK_OF_DECLARE(sun9i_a80_apb1, "allwinner,sun9i-a80-apb1-clk", sun9i_a80_apb1_setup);
 283