uboot/drivers/clk/microchip/mpfs_clk_cfg.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Copyright (C) 2020 Microchip Technology Inc.
   4 * Padmarao Begari <padmarao.begari@microchip.com>
   5 */
   6#include <common.h>
   7#include <clk.h>
   8#include <clk-uclass.h>
   9#include <asm/io.h>
  10#include <dm/device.h>
  11#include <dm/devres.h>
  12#include <dm/uclass.h>
  13#include <dt-bindings/clock/microchip-mpfs-clock.h>
  14#include <linux/err.h>
  15
  16#include "mpfs_clk.h"
  17
  18#define MPFS_CFG_CLOCK "mpfs_cfg_clock"
  19
  20#define REG_CLOCK_CONFIG_CR 0x08
  21
  22/* CPU and AXI clock divisors */
  23static const struct clk_div_table mpfs_div_cpu_axi_table[] = {
  24        { 0, 1 }, { 1, 2 }, { 2, 4 }, { 3, 8 },
  25        { 0, 0 }
  26};
  27
  28/* AHB clock divisors */
  29static const struct clk_div_table mpfs_div_ahb_table[] = {
  30        { 1, 2 }, { 2, 4}, { 3, 8 },
  31        { 0, 0 }
  32};
  33
  34/**
  35 * struct mpfs_cfg_clock - per instance of configuration clock
  36 * @id: index of a configuration clock
  37 * @name: name of a configuration clock
  38 * @shift: shift to the divider bit field of a configuration clock
  39 * @width: width of the divider bit field of a configation clock
  40 * @table: clock divider table instance
  41 * @flags: common clock framework flags
  42 */
  43struct mpfs_cfg_clock {
  44        unsigned int id;
  45        const char *name;
  46        u8 shift;
  47        u8 width;
  48        const struct clk_div_table *table;
  49        unsigned long flags;
  50};
  51
  52/**
  53 * struct mpfs_cfg_hw_clock - hardware configuration clock (cpu, axi, ahb)
  54 * @cfg: configuration clock instance
  55 * @sys_base: base address of the mpfs system register
  56 * @prate: the pll clock rate
  57 * @hw: clock instance
  58 */
  59struct mpfs_cfg_hw_clock {
  60        struct mpfs_cfg_clock cfg;
  61        void __iomem *sys_base;
  62        u32 prate;
  63        struct clk hw;
  64};
  65
  66#define to_mpfs_cfg_clk(_hw) container_of(_hw, struct mpfs_cfg_hw_clock, hw)
  67
  68static ulong mpfs_cfg_clk_recalc_rate(struct clk *hw)
  69{
  70        struct mpfs_cfg_hw_clock *cfg_hw = to_mpfs_cfg_clk(hw);
  71        struct mpfs_cfg_clock *cfg = &cfg_hw->cfg;
  72        void __iomem *base_addr = cfg_hw->sys_base;
  73        unsigned long rate;
  74        u32 val;
  75
  76        val = readl(base_addr + REG_CLOCK_CONFIG_CR) >> cfg->shift;
  77        val &= clk_div_mask(cfg->width);
  78        rate = cfg_hw->prate / (1u << val);
  79        hw->rate = rate;
  80
  81        return rate;
  82}
  83
  84static ulong mpfs_cfg_clk_set_rate(struct clk *hw, ulong rate)
  85{
  86        struct mpfs_cfg_hw_clock *cfg_hw = to_mpfs_cfg_clk(hw);
  87        struct mpfs_cfg_clock *cfg = &cfg_hw->cfg;
  88        void __iomem *base_addr = cfg_hw->sys_base;
  89        u32  val;
  90        int divider_setting;
  91
  92        divider_setting = divider_get_val(rate, cfg_hw->prate, cfg->table, cfg->width, cfg->flags);
  93
  94        if (divider_setting < 0)
  95                return divider_setting;
  96
  97        val = readl(base_addr + REG_CLOCK_CONFIG_CR);
  98        val &= ~(clk_div_mask(cfg->width) << cfg_hw->cfg.shift);
  99        val |= divider_setting << cfg->shift;
 100        writel(val, base_addr + REG_CLOCK_CONFIG_CR);
 101
 102        return clk_get_rate(hw);
 103}
 104
 105#define CLK_CFG(_id, _name, _shift, _width, _table, _flags) {   \
 106                .cfg.id = _id,                                  \
 107                .cfg.name = _name,                              \
 108                .cfg.shift = _shift,                            \
 109                .cfg.width = _width,                            \
 110                .cfg.table = _table,                            \
 111                .cfg.flags = _flags,                            \
 112        }
 113
 114static struct mpfs_cfg_hw_clock mpfs_cfg_clks[] = {
 115        CLK_CFG(CLK_CPU, "clk_cpu", 0, 2, mpfs_div_cpu_axi_table, 0),
 116        CLK_CFG(CLK_AXI, "clk_axi", 2, 2, mpfs_div_cpu_axi_table, 0),
 117        CLK_CFG(CLK_AHB, "clk_ahb", 4, 2, mpfs_div_ahb_table, 0),
 118};
 119
 120int mpfs_clk_register_cfgs(void __iomem *base, struct clk *parent)
 121{
 122        int ret;
 123        int i, id, num_clks;
 124        const char *name;
 125        struct clk *hw;
 126
 127        num_clks = ARRAY_SIZE(mpfs_cfg_clks);
 128        for (i = 0; i < num_clks; i++) {
 129                hw = &mpfs_cfg_clks[i].hw;
 130                mpfs_cfg_clks[i].sys_base = base;
 131                mpfs_cfg_clks[i].prate = clk_get_rate(parent);
 132                name = mpfs_cfg_clks[i].cfg.name;
 133                ret = clk_register(hw, MPFS_CFG_CLOCK, name, parent->dev->name);
 134                if (ret)
 135                        ERR_PTR(ret);
 136                id = mpfs_cfg_clks[i].cfg.id;
 137                clk_dm(id, hw);
 138        }
 139        return 0;
 140}
 141
 142const struct clk_ops mpfs_cfg_clk_ops = {
 143        .set_rate = mpfs_cfg_clk_set_rate,
 144        .get_rate = mpfs_cfg_clk_recalc_rate,
 145};
 146
 147U_BOOT_DRIVER(mpfs_cfg_clock) = {
 148        .name   = MPFS_CFG_CLOCK,
 149        .id     = UCLASS_CLK,
 150        .ops    = &mpfs_cfg_clk_ops,
 151};
 152