uboot/drivers/clk/at91/clk-generic.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Generic clock support for AT91 architectures.
   4 *
   5 * Copyright (C) 2020 Microchip Technology Inc. and its subsidiaries
   6 *
   7 * Author: Claudiu Beznea <claudiu.beznea@microchip.com>
   8 *
   9 * Based on drivers/clk/at91/clk-generated.c from Linux.
  10 */
  11#include <common.h>
  12#include <clk-uclass.h>
  13#include <dm.h>
  14#include <linux/io.h>
  15#include <linux/clk-provider.h>
  16#include <linux/clk/at91_pmc.h>
  17
  18#include "pmc.h"
  19
  20#define UBOOT_DM_CLK_AT91_GCK           "at91-gck-clk"
  21
  22#define GENERATED_MAX_DIV       255
  23
  24struct clk_gck {
  25        void __iomem *base;
  26        const u32 *clk_mux_table;
  27        const u32 *mux_table;
  28        const struct clk_pcr_layout *layout;
  29        struct clk_range range;
  30        struct clk clk;
  31        u32 num_parents;
  32        u32 id;
  33};
  34
  35#define to_clk_gck(_c) container_of(_c, struct clk_gck, clk)
  36
  37static int clk_gck_enable(struct clk *clk)
  38{
  39        struct clk_gck *gck = to_clk_gck(clk);
  40
  41        pmc_write(gck->base, gck->layout->offset,
  42                  (gck->id & gck->layout->pid_mask));
  43        pmc_update_bits(gck->base, gck->layout->offset,
  44                        gck->layout->cmd | AT91_PMC_PCR_GCKEN,
  45                        gck->layout->cmd | AT91_PMC_PCR_GCKEN);
  46
  47        return 0;
  48}
  49
  50static int clk_gck_disable(struct clk *clk)
  51{
  52        struct clk_gck *gck = to_clk_gck(clk);
  53
  54        pmc_write(gck->base, gck->layout->offset,
  55                  (gck->id & gck->layout->pid_mask));
  56        pmc_update_bits(gck->base, gck->layout->offset,
  57                        gck->layout->cmd | AT91_PMC_PCR_GCKEN,
  58                        gck->layout->cmd);
  59
  60        return 0;
  61}
  62
  63static int clk_gck_set_parent(struct clk *clk, struct clk *parent)
  64{
  65        struct clk_gck *gck = to_clk_gck(clk);
  66        int index;
  67
  68        index = at91_clk_mux_val_to_index(gck->clk_mux_table, gck->num_parents,
  69                                          parent->id);
  70        if (index < 0)
  71                return index;
  72
  73        index = at91_clk_mux_index_to_val(gck->mux_table, gck->num_parents,
  74                                          index);
  75        if (index < 0)
  76                return index;
  77
  78        pmc_write(gck->base, gck->layout->offset,
  79                  (gck->id & gck->layout->pid_mask));
  80        pmc_update_bits(gck->base, gck->layout->offset,
  81                        gck->layout->gckcss_mask | gck->layout->cmd,
  82                        (index << (ffs(gck->layout->gckcss_mask) - 1)) |
  83                        gck->layout->cmd);
  84
  85        return 0;
  86}
  87
  88static ulong clk_gck_set_rate(struct clk *clk, ulong rate)
  89{
  90        struct clk_gck *gck = to_clk_gck(clk);
  91        ulong parent_rate = clk_get_parent_rate(clk);
  92        u32 div;
  93
  94        if (!rate || !parent_rate)
  95                return 0;
  96
  97        if (gck->range.max && rate > gck->range.max)
  98                return 0;
  99
 100        div = DIV_ROUND_CLOSEST(parent_rate, rate);
 101        if (div > GENERATED_MAX_DIV + 1 || !div)
 102                return 0;
 103
 104        pmc_write(gck->base, gck->layout->offset,
 105                  (gck->id & gck->layout->pid_mask));
 106        pmc_update_bits(gck->base, gck->layout->offset,
 107                        AT91_PMC_PCR_GCKDIV_MASK | gck->layout->cmd,
 108                        ((div - 1) << (ffs(AT91_PMC_PCR_GCKDIV_MASK) - 1)) |
 109                        gck->layout->cmd);
 110
 111        return parent_rate / div;
 112}
 113
 114static ulong clk_gck_get_rate(struct clk *clk)
 115{
 116        struct clk_gck *gck = to_clk_gck(clk);
 117        ulong parent_rate = clk_get_parent_rate(clk);
 118        u32 val, div;
 119
 120        if (!parent_rate)
 121                return 0;
 122
 123        pmc_write(gck->base, gck->layout->offset,
 124                  (gck->id & gck->layout->pid_mask));
 125        pmc_read(gck->base, gck->layout->offset, &val);
 126
 127        div = (val & AT91_PMC_PCR_GCKDIV_MASK) >>
 128                (ffs(AT91_PMC_PCR_GCKDIV_MASK) - 1);
 129
 130        return parent_rate / (div + 1);
 131}
 132
 133static const struct clk_ops gck_ops = {
 134        .enable = clk_gck_enable,
 135        .disable = clk_gck_disable,
 136        .set_parent = clk_gck_set_parent,
 137        .set_rate = clk_gck_set_rate,
 138        .get_rate = clk_gck_get_rate,
 139};
 140
 141struct clk *
 142at91_clk_register_generic(void __iomem *base,
 143                          const struct clk_pcr_layout *layout,
 144                          const char *name, const char * const *parent_names,
 145                          const u32 *clk_mux_table, const u32 *mux_table,
 146                          u8 num_parents, u8 id,
 147                          const struct clk_range *range)
 148{
 149        struct clk_gck *gck;
 150        struct clk *clk;
 151        int ret, index;
 152        u32 val;
 153
 154        if (!base || !layout || !name || !parent_names || !num_parents ||
 155            !clk_mux_table || !mux_table || !range)
 156                return ERR_PTR(-EINVAL);
 157
 158        gck = kzalloc(sizeof(*gck), GFP_KERNEL);
 159        if (!gck)
 160                return ERR_PTR(-ENOMEM);
 161
 162        gck->id = id;
 163        gck->base = base;
 164        gck->range = *range;
 165        gck->layout = layout;
 166        gck->clk_mux_table = clk_mux_table;
 167        gck->mux_table = mux_table;
 168        gck->num_parents = num_parents;
 169
 170        clk = &gck->clk;
 171        clk->flags = CLK_GET_RATE_NOCACHE;
 172
 173        pmc_write(gck->base, gck->layout->offset,
 174                  (gck->id & gck->layout->pid_mask));
 175        pmc_read(gck->base, gck->layout->offset, &val);
 176
 177        val = (val & gck->layout->gckcss_mask) >>
 178                (ffs(gck->layout->gckcss_mask) - 1);
 179
 180        index = at91_clk_mux_val_to_index(gck->mux_table, gck->num_parents,
 181                                          val);
 182        if (index < 0) {
 183                kfree(gck);
 184                return ERR_PTR(index);
 185        }
 186
 187        ret = clk_register(clk, UBOOT_DM_CLK_AT91_GCK, name,
 188                           parent_names[index]);
 189        if (ret) {
 190                kfree(gck);
 191                clk = ERR_PTR(ret);
 192        }
 193
 194        return clk;
 195}
 196
 197U_BOOT_DRIVER(at91_gck_clk) = {
 198        .name = UBOOT_DM_CLK_AT91_GCK,
 199        .id = UCLASS_CLK,
 200        .ops = &gck_ops,
 201        .flags = DM_FLAG_PRE_RELOC,
 202};
 203