uboot/drivers/clk/at91/clk-programmable.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * Programmable 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-programmable.c from Linux.
  10 */
  11#include <common.h>
  12#include <clk-uclass.h>
  13#include <dm.h>
  14#include <linux/clk-provider.h>
  15#include <linux/clk/at91_pmc.h>
  16
  17#include "pmc.h"
  18
  19#define UBOOT_DM_CLK_AT91_PROG          "at91-prog-clk"
  20
  21#define PROG_ID_MAX             7
  22
  23#define PROG_STATUS_MASK(id)    (1 << ((id) + 8))
  24#define PROG_PRES(_l, _p)       (((_p) >> (_l)->pres_shift) & (_l)->pres_mask)
  25#define PROG_MAX_RM9200_CSS     3
  26
  27struct clk_programmable {
  28        void __iomem *base;
  29        const u32 *clk_mux_table;
  30        const u32 *mux_table;
  31        const struct clk_programmable_layout *layout;
  32        u32 num_parents;
  33        struct clk clk;
  34        u8 id;
  35};
  36
  37#define to_clk_programmable(_c) container_of(_c, struct clk_programmable, clk)
  38
  39static ulong clk_programmable_get_rate(struct clk *clk)
  40{
  41        struct clk_programmable *prog = to_clk_programmable(clk);
  42        const struct clk_programmable_layout *layout = prog->layout;
  43        ulong rate, parent_rate = clk_get_parent_rate(clk);
  44        unsigned int pckr;
  45
  46        pmc_read(prog->base, AT91_PMC_PCKR(prog->id), &pckr);
  47
  48        if (layout->is_pres_direct)
  49                rate = parent_rate / (PROG_PRES(layout, pckr) + 1);
  50        else
  51                rate = parent_rate >> PROG_PRES(layout, pckr);
  52
  53        return rate;
  54}
  55
  56static int clk_programmable_set_parent(struct clk *clk, struct clk *parent)
  57{
  58        struct clk_programmable *prog = to_clk_programmable(clk);
  59        const struct clk_programmable_layout *layout = prog->layout;
  60        unsigned int mask = layout->css_mask;
  61        int index;
  62
  63        index = at91_clk_mux_val_to_index(prog->clk_mux_table,
  64                                          prog->num_parents, parent->id);
  65        if (index < 0)
  66                return index;
  67
  68        index = at91_clk_mux_index_to_val(prog->mux_table, prog->num_parents,
  69                                          index);
  70        if (index < 0)
  71                return index;
  72
  73        if (layout->have_slck_mck)
  74                mask |= AT91_PMC_CSSMCK_MCK;
  75
  76        if (index > layout->css_mask) {
  77                if (index > PROG_MAX_RM9200_CSS && !layout->have_slck_mck)
  78                        return -EINVAL;
  79
  80                index |= AT91_PMC_CSSMCK_MCK;
  81        }
  82
  83        pmc_update_bits(prog->base, AT91_PMC_PCKR(prog->id), mask, index);
  84
  85        return 0;
  86}
  87
  88static ulong clk_programmable_set_rate(struct clk *clk, ulong rate)
  89{
  90        struct clk_programmable *prog = to_clk_programmable(clk);
  91        const struct clk_programmable_layout *layout = prog->layout;
  92        ulong parent_rate = clk_get_parent_rate(clk);
  93        ulong div = parent_rate / rate;
  94        int shift = 0;
  95
  96        if (!parent_rate || !div)
  97                return -EINVAL;
  98
  99        if (layout->is_pres_direct) {
 100                shift = div - 1;
 101
 102                if (shift > layout->pres_mask)
 103                        return -EINVAL;
 104        } else {
 105                shift = fls(div) - 1;
 106
 107                if (div != (1 << shift))
 108                        return -EINVAL;
 109
 110                if (shift >= layout->pres_mask)
 111                        return -EINVAL;
 112        }
 113
 114        pmc_update_bits(prog->base, AT91_PMC_PCKR(prog->id),
 115                        layout->pres_mask << layout->pres_shift,
 116                        shift << layout->pres_shift);
 117
 118        if (layout->is_pres_direct)
 119                return (parent_rate / shift + 1);
 120
 121        return parent_rate >> shift;
 122}
 123
 124static const struct clk_ops programmable_ops = {
 125        .get_rate = clk_programmable_get_rate,
 126        .set_parent = clk_programmable_set_parent,
 127        .set_rate = clk_programmable_set_rate,
 128};
 129
 130struct clk *at91_clk_register_programmable(void __iomem *base, const char *name,
 131                        const char *const *parent_names, u8 num_parents, u8 id,
 132                        const struct clk_programmable_layout *layout,
 133                        const u32 *clk_mux_table, const u32 *mux_table)
 134{
 135        struct clk_programmable *prog;
 136        struct clk *clk;
 137        u32 val, tmp;
 138        int ret;
 139
 140        if (!base || !name || !parent_names || !num_parents ||
 141            !layout || !clk_mux_table || !mux_table || id > PROG_ID_MAX)
 142                return ERR_PTR(-EINVAL);
 143
 144        prog = kzalloc(sizeof(*prog), GFP_KERNEL);
 145        if (!prog)
 146                return ERR_PTR(-ENOMEM);
 147
 148        prog->id = id;
 149        prog->layout = layout;
 150        prog->base = base;
 151        prog->clk_mux_table = clk_mux_table;
 152        prog->mux_table = mux_table;
 153        prog->num_parents = num_parents;
 154
 155        pmc_read(prog->base, AT91_PMC_PCKR(prog->id), &tmp);
 156        val = tmp & prog->layout->css_mask;
 157        if (layout->have_slck_mck && (tmp & AT91_PMC_CSSMCK_MCK) && !val)
 158                ret = PROG_MAX_RM9200_CSS + 1;
 159        else
 160                ret = at91_clk_mux_val_to_index(prog->mux_table,
 161                                                prog->num_parents, val);
 162        if (ret < 0) {
 163                kfree(prog);
 164                return ERR_PTR(ret);
 165        }
 166
 167        clk = &prog->clk;
 168        clk->flags = CLK_GET_RATE_NOCACHE;
 169        ret = clk_register(clk, UBOOT_DM_CLK_AT91_PROG, name,
 170                           parent_names[ret]);
 171        if (ret) {
 172                kfree(prog);
 173                clk = ERR_PTR(ret);
 174        }
 175
 176        return clk;
 177}
 178
 179U_BOOT_DRIVER(at91_prog_clk) = {
 180        .name = UBOOT_DM_CLK_AT91_PROG,
 181        .id = UCLASS_CLK,
 182        .ops = &programmable_ops,
 183        .flags = DM_FLAG_PRE_RELOC,
 184};
 185
 186const struct clk_programmable_layout at91rm9200_programmable_layout = {
 187        .pres_mask = 0x7,
 188        .pres_shift = 2,
 189        .css_mask = 0x3,
 190        .have_slck_mck = 0,
 191        .is_pres_direct = 0,
 192};
 193
 194const struct clk_programmable_layout at91sam9g45_programmable_layout = {
 195        .pres_mask = 0x7,
 196        .pres_shift = 2,
 197        .css_mask = 0x3,
 198        .have_slck_mck = 1,
 199        .is_pres_direct = 0,
 200};
 201
 202const struct clk_programmable_layout at91sam9x5_programmable_layout = {
 203        .pres_mask = 0x7,
 204        .pres_shift = 4,
 205        .css_mask = 0x7,
 206        .have_slck_mck = 0,
 207        .is_pres_direct = 0,
 208};
 209