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