1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright (C) 2017, Intel Corporation 4 */ 5 #include <linux/clk-provider.h> 6 #include <linux/slab.h> 7 #include "stratix10-clk.h" 8 #include "clk.h" 9 10 #define SOCFPGA_CS_PDBG_CLK "cs_pdbg_clk" 11 #define to_socfpga_gate_clk(p) container_of(p, struct socfpga_gate_clk, hw.hw) 12 13 static unsigned long socfpga_gate_clk_recalc_rate(struct clk_hw *hwclk, 14 unsigned long parent_rate) 15 { 16 struct socfpga_gate_clk *socfpgaclk = to_socfpga_gate_clk(hwclk); 17 u32 div = 1, val; 18 19 if (socfpgaclk->fixed_div) { 20 div = socfpgaclk->fixed_div; 21 } else if (socfpgaclk->div_reg) { 22 val = readl(socfpgaclk->div_reg) >> socfpgaclk->shift; 23 val &= GENMASK(socfpgaclk->width - 1, 0); 24 div = (1 << val); 25 } 26 return parent_rate / div; 27 } 28 29 static unsigned long socfpga_dbg_clk_recalc_rate(struct clk_hw *hwclk, 30 unsigned long parent_rate) 31 { 32 struct socfpga_gate_clk *socfpgaclk = to_socfpga_gate_clk(hwclk); 33 u32 div = 1, val; 34 35 val = readl(socfpgaclk->div_reg) >> socfpgaclk->shift; 36 val &= GENMASK(socfpgaclk->width - 1, 0); 37 div = (1 << val); 38 div = div ? 4 : 1; 39 40 return parent_rate / div; 41 } 42 43 static u8 socfpga_gate_get_parent(struct clk_hw *hwclk) 44 { 45 struct socfpga_gate_clk *socfpgaclk = to_socfpga_gate_clk(hwclk); 46 u32 mask; 47 u8 parent = 0; 48 49 if (socfpgaclk->bypass_reg) { 50 mask = (0x1 << socfpgaclk->bypass_shift); 51 parent = ((readl(socfpgaclk->bypass_reg) & mask) >> 52 socfpgaclk->bypass_shift); 53 } 54 return parent; 55 } 56 57 static struct clk_ops gateclk_ops = { 58 .recalc_rate = socfpga_gate_clk_recalc_rate, 59 .get_parent = socfpga_gate_get_parent, 60 }; 61 62 static const struct clk_ops dbgclk_ops = { 63 .recalc_rate = socfpga_dbg_clk_recalc_rate, 64 .get_parent = socfpga_gate_get_parent, 65 }; 66 67 struct clk *s10_register_gate(const char *name, const char *parent_name, 68 const char * const *parent_names, 69 u8 num_parents, unsigned long flags, 70 void __iomem *regbase, unsigned long gate_reg, 71 unsigned long gate_idx, unsigned long div_reg, 72 unsigned long div_offset, u8 div_width, 73 unsigned long bypass_reg, u8 bypass_shift, 74 u8 fixed_div) 75 { 76 struct clk *clk; 77 struct socfpga_gate_clk *socfpga_clk; 78 struct clk_init_data init; 79 80 socfpga_clk = kzalloc(sizeof(*socfpga_clk), GFP_KERNEL); 81 if (!socfpga_clk) 82 return NULL; 83 84 socfpga_clk->hw.reg = regbase + gate_reg; 85 socfpga_clk->hw.bit_idx = gate_idx; 86 87 gateclk_ops.enable = clk_gate_ops.enable; 88 gateclk_ops.disable = clk_gate_ops.disable; 89 90 socfpga_clk->fixed_div = fixed_div; 91 92 if (div_reg) 93 socfpga_clk->div_reg = regbase + div_reg; 94 else 95 socfpga_clk->div_reg = NULL; 96 97 socfpga_clk->width = div_width; 98 socfpga_clk->shift = div_offset; 99 100 if (bypass_reg) 101 socfpga_clk->bypass_reg = regbase + bypass_reg; 102 else 103 socfpga_clk->bypass_reg = NULL; 104 socfpga_clk->bypass_shift = bypass_shift; 105 106 if (streq(name, "cs_pdbg_clk")) 107 init.ops = &dbgclk_ops; 108 else 109 init.ops = &gateclk_ops; 110 111 init.name = name; 112 init.flags = flags; 113 114 init.num_parents = num_parents; 115 init.parent_names = parent_names ? parent_names : &parent_name; 116 socfpga_clk->hw.hw.init = &init; 117 118 clk = clk_register(NULL, &socfpga_clk->hw.hw); 119 if (WARN_ON(IS_ERR(clk))) { 120 kfree(socfpga_clk); 121 return NULL; 122 } 123 124 return clk; 125 } 126