1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * Copyright (C) 2022 Microchip Technology Inc. 4 */ 5#include <common.h> 6#include <clk.h> 7#include <clk-uclass.h> 8#include <asm/io.h> 9#include <dm/device.h> 10#include <dm/devres.h> 11#include <dm/uclass.h> 12#include <dt-bindings/clock/microchip-mpfs-clock.h> 13#include <linux/err.h> 14 15#include "mpfs_clk.h" 16 17#define MPFS_MSSPLL_CLOCK "mpfs_msspll_clock" 18 19/* address offset of control registers */ 20#define REG_MSSPLL_REF_CR 0x08u 21#define REG_MSSPLL_POSTDIV_CR 0x10u 22#define REG_MSSPLL_SSCG_2_CR 0x2Cu 23 24#define MSSPLL_FBDIV_SHIFT 0x00u 25#define MSSPLL_FBDIV_WIDTH 0x0Cu 26#define MSSPLL_REFDIV_SHIFT 0x08u 27#define MSSPLL_REFDIV_WIDTH 0x06u 28#define MSSPLL_POSTDIV_SHIFT 0x08u 29#define MSSPLL_POSTDIV_WIDTH 0x07u 30#define MSSPLL_FIXED_DIV 4u 31 32/** 33 * struct mpfs_msspll_hw_clock 34 * @id: index of the msspll clock 35 * @name: the msspll clocks name 36 * @reg_offset: offset to the core complex's output of the msspll 37 * @shift: shift to the divider bit field of a msspll clock output 38 * @width: width of the divider bit field of the msspll clock output 39 * @flags: common clock framework flags 40 * @prate: the reference clock rate 41 * @hw: clock instance 42 */ 43struct mpfs_msspll_hw_clock { 44 void __iomem *base; 45 unsigned int id; 46 const char *name; 47 u32 reg_offset; 48 u32 shift; 49 u32 width; 50 u32 flags; 51 u32 prate; 52 struct clk hw; 53}; 54 55#define to_mpfs_msspll_clk(_hw) container_of(_hw, struct mpfs_msspll_hw_clock, hw) 56 57static unsigned long mpfs_clk_msspll_recalc_rate(struct clk *hw) 58{ 59 struct mpfs_msspll_hw_clock *msspll_hw = to_mpfs_msspll_clk(hw); 60 void __iomem *mult_addr = msspll_hw->base + msspll_hw->reg_offset; 61 void __iomem *ref_div_addr = msspll_hw->base + REG_MSSPLL_REF_CR; 62 void __iomem *postdiv_addr = msspll_hw->base + REG_MSSPLL_POSTDIV_CR; 63 u32 mult, ref_div, postdiv; 64 unsigned long temp; 65 66 mult = readl(mult_addr) >> MSSPLL_FBDIV_SHIFT; 67 mult &= clk_div_mask(MSSPLL_FBDIV_WIDTH); 68 ref_div = readl(ref_div_addr) >> MSSPLL_REFDIV_SHIFT; 69 ref_div &= clk_div_mask(MSSPLL_REFDIV_WIDTH); 70 postdiv = readl(postdiv_addr) >> MSSPLL_POSTDIV_SHIFT; 71 postdiv &= clk_div_mask(MSSPLL_POSTDIV_WIDTH); 72 73 temp = msspll_hw->prate / (ref_div * MSSPLL_FIXED_DIV * postdiv); 74 return temp * mult; 75} 76 77#define CLK_PLL(_id, _name, _shift, _width, _reg_offset, _flags) { \ 78 .id = _id, \ 79 .name = _name, \ 80 .shift = _shift, \ 81 .width = _width, \ 82 .reg_offset = _reg_offset, \ 83 .flags = _flags, \ 84} 85 86static struct mpfs_msspll_hw_clock mpfs_msspll_clks[] = { 87 CLK_PLL(CLK_MSSPLL, "clk_msspll", MSSPLL_FBDIV_SHIFT, 88 MSSPLL_FBDIV_WIDTH, REG_MSSPLL_SSCG_2_CR, 0), 89}; 90 91int mpfs_clk_register_msspll(void __iomem *base, struct clk *parent) 92{ 93 int id, ret; 94 const char *name; 95 struct clk *hw; 96 97 hw = &mpfs_msspll_clks[0].hw; 98 mpfs_msspll_clks[0].base = base; 99 mpfs_msspll_clks[0].prate = clk_get_rate(parent); 100 name = mpfs_msspll_clks[0].name; 101 ret = clk_register(hw, MPFS_MSSPLL_CLOCK, name, parent->dev->name); 102 if (ret) 103 ERR_PTR(ret); 104 id = mpfs_msspll_clks[0].id; 105 clk_dm(id, hw); 106 107 return 0; 108} 109 110const struct clk_ops mpfs_msspll_clk_ops = { 111 .get_rate = mpfs_clk_msspll_recalc_rate, 112}; 113 114U_BOOT_DRIVER(mpfs_msspll_clock) = { 115 .name = MPFS_MSSPLL_CLOCK, 116 .id = UCLASS_CLK, 117 .ops = &mpfs_msspll_clk_ops, 118}; 119 120