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