uboot/drivers/clk/nuvoton/clk_npcm.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Copyright (c) 2022 Nuvoton Technology Corp.
   4 *
   5 * Formula for calculating clock rate:
   6 * Fout = ((Fin / PRE_DIV) / div) / POST_DIV
   7 */
   8
   9#include <div64.h>
  10#include <dm.h>
  11#include <asm/io.h>
  12#include <linux/bitfield.h>
  13#include <linux/log2.h>
  14#include "clk_npcm.h"
  15
  16static int clkid_to_clksel(struct npcm_clk_select *selector, int id)
  17{
  18        int i;
  19
  20        for (i = 0; i < selector->num_parents; i++) {
  21                if (selector->parents[i].id == id)
  22                        return selector->parents[i].clksel;
  23        }
  24
  25        return -EINVAL;
  26}
  27
  28static int clksel_to_clkid(struct npcm_clk_select *selector, int clksel)
  29{
  30        int i;
  31
  32        for (i = 0; i < selector->num_parents; i++) {
  33                if (selector->parents[i].clksel == clksel)
  34                        return selector->parents[i].id;
  35        }
  36
  37        return -EINVAL;
  38}
  39
  40static struct npcm_clk_pll *npcm_clk_pll_get(struct npcm_clk_data *clk_data, int id)
  41{
  42        struct npcm_clk_pll *pll = clk_data->clk_plls;
  43        int i;
  44
  45        for (i = 0; i < clk_data->num_plls; i++) {
  46                if (pll->id == id)
  47                        return pll;
  48                pll++;
  49        }
  50
  51        return NULL;
  52}
  53
  54static struct npcm_clk_select *npcm_clk_selector_get(struct npcm_clk_data *clk_data,
  55                                                     int id)
  56{
  57        struct npcm_clk_select *selector = clk_data->clk_selectors;
  58        int i;
  59
  60        for (i = 0; i < clk_data->num_selectors; i++) {
  61                if (selector->id == id)
  62                        return selector;
  63                selector++;
  64        }
  65
  66        return NULL;
  67}
  68
  69static struct npcm_clk_div *npcm_clk_divider_get(struct npcm_clk_data *clk_data,
  70                                                 int id)
  71{
  72        struct npcm_clk_div *divider = clk_data->clk_dividers;
  73        int i;
  74
  75        for (i = 0; i < clk_data->num_dividers; i++) {
  76                if (divider->id == id)
  77                        return divider;
  78                divider++;
  79        }
  80
  81        return NULL;
  82}
  83
  84static ulong npcm_clk_get_fin(struct clk *clk)
  85{
  86        struct npcm_clk_priv *priv = dev_get_priv(clk->dev);
  87        struct npcm_clk_select *selector;
  88        struct clk parent;
  89        ulong parent_rate;
  90        u32 val, clksel;
  91        int ret;
  92
  93        selector = npcm_clk_selector_get(priv->clk_data, clk->id);
  94        if (!selector)
  95                return 0;
  96
  97        if (selector->flags & FIXED_PARENT) {
  98                clksel = 0;
  99        } else {
 100                val = readl(priv->base + selector->reg);
 101                clksel = (val & selector->mask) >> (ffs(selector->mask) - 1);
 102        }
 103        parent.id = clksel_to_clkid(selector, clksel);
 104
 105        ret = clk_request(clk->dev, &parent);
 106        if (ret)
 107                return 0;
 108
 109        parent_rate = clk_get_rate(&parent);
 110
 111        debug("fin of clk%lu = %lu\n", clk->id, parent_rate);
 112        return parent_rate;
 113}
 114
 115static u32 npcm_clk_get_div(struct clk *clk)
 116{
 117        struct npcm_clk_priv *priv = dev_get_priv(clk->dev);
 118        struct npcm_clk_div *divider;
 119        u32 val, div;
 120
 121        divider = npcm_clk_divider_get(priv->clk_data, clk->id);
 122        if (!divider)
 123                return 0;
 124
 125        val = readl(priv->base + divider->reg);
 126        div = (val & divider->mask) >> (ffs(divider->mask) - 1);
 127        if (divider->flags & DIV_TYPE1)
 128                div = div + 1;
 129        else
 130                div = 1 << div;
 131
 132        if (divider->flags & PRE_DIV2)
 133                div = div << 1;
 134
 135        return div;
 136}
 137
 138static u32 npcm_clk_set_div(struct clk *clk, u32 div)
 139{
 140        struct npcm_clk_priv *priv = dev_get_priv(clk->dev);
 141        struct npcm_clk_div *divider;
 142        u32 val, clkdiv;
 143
 144        divider = npcm_clk_divider_get(priv->clk_data, clk->id);
 145        if (!divider)
 146                return -EINVAL;
 147
 148        if (divider->flags & PRE_DIV2)
 149                div = div >> 1;
 150
 151        if (divider->flags & DIV_TYPE1)
 152                clkdiv = div - 1;
 153        else
 154                clkdiv = ilog2(div);
 155
 156        val = readl(priv->base + divider->reg);
 157        val &= ~divider->mask;
 158        val |= (clkdiv << (ffs(divider->mask) - 1)) & divider->mask;
 159        writel(val, priv->base + divider->reg);
 160
 161        return 0;
 162}
 163
 164static ulong npcm_clk_get_fout(struct clk *clk)
 165{
 166        ulong parent_rate;
 167        u32 div;
 168
 169        parent_rate = npcm_clk_get_fin(clk);
 170        if (!parent_rate)
 171                return -EINVAL;
 172
 173        div = npcm_clk_get_div(clk);
 174        if (!div)
 175                return -EINVAL;
 176
 177        debug("fout of clk%lu = (%lu / %u)\n", clk->id, parent_rate, div);
 178        return (parent_rate / div);
 179}
 180
 181static ulong npcm_clk_get_pll_fout(struct clk *clk)
 182{
 183        struct npcm_clk_priv *priv = dev_get_priv(clk->dev);
 184        struct npcm_clk_pll *pll;
 185        struct clk parent;
 186        ulong parent_rate;
 187        ulong fbdv, indv, otdv1, otdv2;
 188        u32 val;
 189        u64 ret;
 190
 191        pll = npcm_clk_pll_get(priv->clk_data, clk->id);
 192        if (!pll)
 193                return -ENODEV;
 194
 195        parent.id = pll->parent_id;
 196        ret = clk_request(clk->dev, &parent);
 197        if (ret)
 198                return ret;
 199
 200        parent_rate = clk_get_rate(&parent);
 201
 202        val = readl(priv->base + pll->reg);
 203        indv = FIELD_GET(PLLCON_INDV, val);
 204        fbdv = FIELD_GET(PLLCON_FBDV, val);
 205        otdv1 = FIELD_GET(PLLCON_OTDV1, val);
 206        otdv2 = FIELD_GET(PLLCON_OTDV2, val);
 207
 208        ret = (u64)parent_rate * fbdv;
 209        do_div(ret, indv * otdv1 * otdv2);
 210        if (pll->flags & POST_DIV2)
 211                do_div(ret, 2);
 212
 213        debug("fout of pll(id %lu) = %llu\n", clk->id, ret);
 214        return ret;
 215}
 216
 217static ulong npcm_clk_get_rate(struct clk *clk)
 218{
 219        struct npcm_clk_priv *priv = dev_get_priv(clk->dev);
 220        struct npcm_clk_data *clk_data = priv->clk_data;
 221        struct clk refclk;
 222        int ret;
 223
 224        debug("%s: id %lu\n", __func__, clk->id);
 225        if (clk->id == clk_data->refclk_id) {
 226                ret = clk_get_by_name(clk->dev, "refclk", &refclk);
 227                if (!ret)
 228                        return clk_get_rate(&refclk);
 229                else
 230                        return ret;
 231        }
 232
 233        if (clk->id >= clk_data->pll0_id &&
 234            clk->id < clk_data->pll0_id + clk_data->num_plls)
 235                return npcm_clk_get_pll_fout(clk);
 236        else
 237                return npcm_clk_get_fout(clk);
 238}
 239
 240static ulong npcm_clk_set_rate(struct clk *clk, ulong rate)
 241{
 242        ulong parent_rate;
 243        u32 div;
 244        int ret;
 245
 246        debug("%s: id %lu, rate %lu\n", __func__, clk->id, rate);
 247        parent_rate = npcm_clk_get_fin(clk);
 248        if (!parent_rate)
 249                return -EINVAL;
 250
 251        div = DIV_ROUND_UP(parent_rate, rate);
 252        ret = npcm_clk_set_div(clk, div);
 253        if (ret)
 254                return ret;
 255
 256        debug("%s: rate %lu, new rate (%lu / %u)\n", __func__, rate, parent_rate, div);
 257        return (parent_rate / div);
 258}
 259
 260static int npcm_clk_set_parent(struct clk *clk, struct clk *parent)
 261{
 262        struct npcm_clk_priv *priv = dev_get_priv(clk->dev);
 263        struct npcm_clk_select *selector;
 264        int clksel;
 265        u32 val;
 266
 267        debug("%s: id %lu, parent %lu\n", __func__, clk->id, parent->id);
 268        selector = npcm_clk_selector_get(priv->clk_data, clk->id);
 269        if (!selector)
 270                return -EINVAL;
 271
 272        clksel = clkid_to_clksel(selector, parent->id);
 273        if (clksel < 0)
 274                return -EINVAL;
 275
 276        val = readl(priv->base + selector->reg);
 277        val &= ~selector->mask;
 278        val |= clksel << (ffs(selector->mask) - 1);
 279        writel(val, priv->base + selector->reg);
 280
 281        return 0;
 282}
 283
 284static int npcm_clk_request(struct clk *clk)
 285{
 286        struct npcm_clk_priv *priv = dev_get_priv(clk->dev);
 287
 288        if (clk->id >= priv->num_clks)
 289                return -EINVAL;
 290
 291        return 0;
 292}
 293
 294const struct clk_ops npcm_clk_ops = {
 295        .get_rate = npcm_clk_get_rate,
 296        .set_rate = npcm_clk_set_rate,
 297        .set_parent = npcm_clk_set_parent,
 298        .request = npcm_clk_request,
 299};
 300