162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2017, Intel Corporation 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci#include <linux/clk-provider.h> 662306a36Sopenharmony_ci#include <linux/io.h> 762306a36Sopenharmony_ci#include <linux/slab.h> 862306a36Sopenharmony_ci#include "stratix10-clk.h" 962306a36Sopenharmony_ci#include "clk.h" 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#define SOCFPGA_CS_PDBG_CLK "cs_pdbg_clk" 1262306a36Sopenharmony_ci#define to_socfpga_gate_clk(p) container_of(p, struct socfpga_gate_clk, hw.hw) 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#define SOCFPGA_EMAC0_CLK "emac0_clk" 1562306a36Sopenharmony_ci#define SOCFPGA_EMAC1_CLK "emac1_clk" 1662306a36Sopenharmony_ci#define SOCFPGA_EMAC2_CLK "emac2_clk" 1762306a36Sopenharmony_ci#define AGILEX_BYPASS_OFFSET 0xC 1862306a36Sopenharmony_ci#define STRATIX10_BYPASS_OFFSET 0x2C 1962306a36Sopenharmony_ci#define BOOTCLK_BYPASS 2 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_cistatic unsigned long socfpga_gate_clk_recalc_rate(struct clk_hw *hwclk, 2262306a36Sopenharmony_ci unsigned long parent_rate) 2362306a36Sopenharmony_ci{ 2462306a36Sopenharmony_ci struct socfpga_gate_clk *socfpgaclk = to_socfpga_gate_clk(hwclk); 2562306a36Sopenharmony_ci u32 div = 1, val; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci if (socfpgaclk->fixed_div) { 2862306a36Sopenharmony_ci div = socfpgaclk->fixed_div; 2962306a36Sopenharmony_ci } else if (socfpgaclk->div_reg) { 3062306a36Sopenharmony_ci val = readl(socfpgaclk->div_reg) >> socfpgaclk->shift; 3162306a36Sopenharmony_ci val &= GENMASK(socfpgaclk->width - 1, 0); 3262306a36Sopenharmony_ci div = (1 << val); 3362306a36Sopenharmony_ci } 3462306a36Sopenharmony_ci return parent_rate / div; 3562306a36Sopenharmony_ci} 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistatic unsigned long socfpga_dbg_clk_recalc_rate(struct clk_hw *hwclk, 3862306a36Sopenharmony_ci unsigned long parent_rate) 3962306a36Sopenharmony_ci{ 4062306a36Sopenharmony_ci struct socfpga_gate_clk *socfpgaclk = to_socfpga_gate_clk(hwclk); 4162306a36Sopenharmony_ci u32 div, val; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci val = readl(socfpgaclk->div_reg) >> socfpgaclk->shift; 4462306a36Sopenharmony_ci val &= GENMASK(socfpgaclk->width - 1, 0); 4562306a36Sopenharmony_ci div = (1 << val); 4662306a36Sopenharmony_ci div = div ? 4 : 1; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci return parent_rate / div; 4962306a36Sopenharmony_ci} 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_cistatic u8 socfpga_gate_get_parent(struct clk_hw *hwclk) 5262306a36Sopenharmony_ci{ 5362306a36Sopenharmony_ci struct socfpga_gate_clk *socfpgaclk = to_socfpga_gate_clk(hwclk); 5462306a36Sopenharmony_ci u32 mask, second_bypass; 5562306a36Sopenharmony_ci u8 parent = 0; 5662306a36Sopenharmony_ci const char *name = clk_hw_get_name(hwclk); 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci if (socfpgaclk->bypass_reg) { 5962306a36Sopenharmony_ci mask = (0x1 << socfpgaclk->bypass_shift); 6062306a36Sopenharmony_ci parent = ((readl(socfpgaclk->bypass_reg) & mask) >> 6162306a36Sopenharmony_ci socfpgaclk->bypass_shift); 6262306a36Sopenharmony_ci } 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci if (streq(name, SOCFPGA_EMAC0_CLK) || 6562306a36Sopenharmony_ci streq(name, SOCFPGA_EMAC1_CLK) || 6662306a36Sopenharmony_ci streq(name, SOCFPGA_EMAC2_CLK)) { 6762306a36Sopenharmony_ci second_bypass = readl(socfpgaclk->bypass_reg - 6862306a36Sopenharmony_ci STRATIX10_BYPASS_OFFSET); 6962306a36Sopenharmony_ci /* EMACA bypass to bootclk @0xB0 offset */ 7062306a36Sopenharmony_ci if (second_bypass & 0x1) 7162306a36Sopenharmony_ci if (parent == 0) /* only applicable if parent is maca */ 7262306a36Sopenharmony_ci parent = BOOTCLK_BYPASS; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci if (second_bypass & 0x2) 7562306a36Sopenharmony_ci if (parent == 1) /* only applicable if parent is macb */ 7662306a36Sopenharmony_ci parent = BOOTCLK_BYPASS; 7762306a36Sopenharmony_ci } 7862306a36Sopenharmony_ci return parent; 7962306a36Sopenharmony_ci} 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cistatic u8 socfpga_agilex_gate_get_parent(struct clk_hw *hwclk) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci struct socfpga_gate_clk *socfpgaclk = to_socfpga_gate_clk(hwclk); 8462306a36Sopenharmony_ci u32 mask, second_bypass; 8562306a36Sopenharmony_ci u8 parent = 0; 8662306a36Sopenharmony_ci const char *name = clk_hw_get_name(hwclk); 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci if (socfpgaclk->bypass_reg) { 8962306a36Sopenharmony_ci mask = (0x1 << socfpgaclk->bypass_shift); 9062306a36Sopenharmony_ci parent = ((readl(socfpgaclk->bypass_reg) & mask) >> 9162306a36Sopenharmony_ci socfpgaclk->bypass_shift); 9262306a36Sopenharmony_ci } 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci if (streq(name, SOCFPGA_EMAC0_CLK) || 9562306a36Sopenharmony_ci streq(name, SOCFPGA_EMAC1_CLK) || 9662306a36Sopenharmony_ci streq(name, SOCFPGA_EMAC2_CLK)) { 9762306a36Sopenharmony_ci second_bypass = readl(socfpgaclk->bypass_reg - 9862306a36Sopenharmony_ci AGILEX_BYPASS_OFFSET); 9962306a36Sopenharmony_ci /* EMACA bypass to bootclk @0x88 offset */ 10062306a36Sopenharmony_ci if (second_bypass & 0x1) 10162306a36Sopenharmony_ci if (parent == 0) /* only applicable if parent is maca */ 10262306a36Sopenharmony_ci parent = BOOTCLK_BYPASS; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci if (second_bypass & 0x2) 10562306a36Sopenharmony_ci if (parent == 1) /* only applicable if parent is macb */ 10662306a36Sopenharmony_ci parent = BOOTCLK_BYPASS; 10762306a36Sopenharmony_ci } 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci return parent; 11062306a36Sopenharmony_ci} 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_cistatic struct clk_ops gateclk_ops = { 11362306a36Sopenharmony_ci .recalc_rate = socfpga_gate_clk_recalc_rate, 11462306a36Sopenharmony_ci .get_parent = socfpga_gate_get_parent, 11562306a36Sopenharmony_ci}; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_cistatic const struct clk_ops agilex_gateclk_ops = { 11862306a36Sopenharmony_ci .recalc_rate = socfpga_gate_clk_recalc_rate, 11962306a36Sopenharmony_ci .get_parent = socfpga_agilex_gate_get_parent, 12062306a36Sopenharmony_ci}; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cistatic const struct clk_ops dbgclk_ops = { 12362306a36Sopenharmony_ci .recalc_rate = socfpga_dbg_clk_recalc_rate, 12462306a36Sopenharmony_ci .get_parent = socfpga_gate_get_parent, 12562306a36Sopenharmony_ci}; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_cistruct clk_hw *s10_register_gate(const struct stratix10_gate_clock *clks, void __iomem *regbase) 12862306a36Sopenharmony_ci{ 12962306a36Sopenharmony_ci struct clk_hw *hw_clk; 13062306a36Sopenharmony_ci struct socfpga_gate_clk *socfpga_clk; 13162306a36Sopenharmony_ci struct clk_init_data init; 13262306a36Sopenharmony_ci const char *parent_name = clks->parent_name; 13362306a36Sopenharmony_ci int ret; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci socfpga_clk = kzalloc(sizeof(*socfpga_clk), GFP_KERNEL); 13662306a36Sopenharmony_ci if (!socfpga_clk) 13762306a36Sopenharmony_ci return NULL; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci socfpga_clk->hw.reg = regbase + clks->gate_reg; 14062306a36Sopenharmony_ci socfpga_clk->hw.bit_idx = clks->gate_idx; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci gateclk_ops.enable = clk_gate_ops.enable; 14362306a36Sopenharmony_ci gateclk_ops.disable = clk_gate_ops.disable; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci socfpga_clk->fixed_div = clks->fixed_div; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci if (clks->div_reg) 14862306a36Sopenharmony_ci socfpga_clk->div_reg = regbase + clks->div_reg; 14962306a36Sopenharmony_ci else 15062306a36Sopenharmony_ci socfpga_clk->div_reg = NULL; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci socfpga_clk->width = clks->div_width; 15362306a36Sopenharmony_ci socfpga_clk->shift = clks->div_offset; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci if (clks->bypass_reg) 15662306a36Sopenharmony_ci socfpga_clk->bypass_reg = regbase + clks->bypass_reg; 15762306a36Sopenharmony_ci else 15862306a36Sopenharmony_ci socfpga_clk->bypass_reg = NULL; 15962306a36Sopenharmony_ci socfpga_clk->bypass_shift = clks->bypass_shift; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci if (streq(clks->name, "cs_pdbg_clk")) 16262306a36Sopenharmony_ci init.ops = &dbgclk_ops; 16362306a36Sopenharmony_ci else 16462306a36Sopenharmony_ci init.ops = &gateclk_ops; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci init.name = clks->name; 16762306a36Sopenharmony_ci init.flags = clks->flags; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci init.num_parents = clks->num_parents; 17062306a36Sopenharmony_ci init.parent_names = parent_name ? &parent_name : NULL; 17162306a36Sopenharmony_ci if (init.parent_names == NULL) 17262306a36Sopenharmony_ci init.parent_data = clks->parent_data; 17362306a36Sopenharmony_ci socfpga_clk->hw.hw.init = &init; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci hw_clk = &socfpga_clk->hw.hw; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci ret = clk_hw_register(NULL, &socfpga_clk->hw.hw); 17862306a36Sopenharmony_ci if (ret) { 17962306a36Sopenharmony_ci kfree(socfpga_clk); 18062306a36Sopenharmony_ci return ERR_PTR(ret); 18162306a36Sopenharmony_ci } 18262306a36Sopenharmony_ci return hw_clk; 18362306a36Sopenharmony_ci} 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_cistruct clk_hw *agilex_register_gate(const struct stratix10_gate_clock *clks, void __iomem *regbase) 18662306a36Sopenharmony_ci{ 18762306a36Sopenharmony_ci struct clk_hw *hw_clk; 18862306a36Sopenharmony_ci struct socfpga_gate_clk *socfpga_clk; 18962306a36Sopenharmony_ci struct clk_init_data init; 19062306a36Sopenharmony_ci const char *parent_name = clks->parent_name; 19162306a36Sopenharmony_ci int ret; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci socfpga_clk = kzalloc(sizeof(*socfpga_clk), GFP_KERNEL); 19462306a36Sopenharmony_ci if (!socfpga_clk) 19562306a36Sopenharmony_ci return NULL; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci socfpga_clk->hw.reg = regbase + clks->gate_reg; 19862306a36Sopenharmony_ci socfpga_clk->hw.bit_idx = clks->gate_idx; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci gateclk_ops.enable = clk_gate_ops.enable; 20162306a36Sopenharmony_ci gateclk_ops.disable = clk_gate_ops.disable; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci socfpga_clk->fixed_div = clks->fixed_div; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci if (clks->div_reg) 20662306a36Sopenharmony_ci socfpga_clk->div_reg = regbase + clks->div_reg; 20762306a36Sopenharmony_ci else 20862306a36Sopenharmony_ci socfpga_clk->div_reg = NULL; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci socfpga_clk->width = clks->div_width; 21162306a36Sopenharmony_ci socfpga_clk->shift = clks->div_offset; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci if (clks->bypass_reg) 21462306a36Sopenharmony_ci socfpga_clk->bypass_reg = regbase + clks->bypass_reg; 21562306a36Sopenharmony_ci else 21662306a36Sopenharmony_ci socfpga_clk->bypass_reg = NULL; 21762306a36Sopenharmony_ci socfpga_clk->bypass_shift = clks->bypass_shift; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci if (streq(clks->name, "cs_pdbg_clk")) 22062306a36Sopenharmony_ci init.ops = &dbgclk_ops; 22162306a36Sopenharmony_ci else 22262306a36Sopenharmony_ci init.ops = &agilex_gateclk_ops; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci init.name = clks->name; 22562306a36Sopenharmony_ci init.flags = clks->flags; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci init.num_parents = clks->num_parents; 22862306a36Sopenharmony_ci init.parent_names = parent_name ? &parent_name : NULL; 22962306a36Sopenharmony_ci if (init.parent_names == NULL) 23062306a36Sopenharmony_ci init.parent_data = clks->parent_data; 23162306a36Sopenharmony_ci socfpga_clk->hw.hw.init = &init; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci hw_clk = &socfpga_clk->hw.hw; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci ret = clk_hw_register(NULL, &socfpga_clk->hw.hw); 23662306a36Sopenharmony_ci if (ret) { 23762306a36Sopenharmony_ci kfree(socfpga_clk); 23862306a36Sopenharmony_ci return ERR_PTR(ret); 23962306a36Sopenharmony_ci } 24062306a36Sopenharmony_ci return hw_clk; 24162306a36Sopenharmony_ci} 242