162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2017, Intel Corporation 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci#include <linux/slab.h> 662306a36Sopenharmony_ci#include <linux/clk-provider.h> 762306a36Sopenharmony_ci#include <linux/io.h> 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include "stratix10-clk.h" 1062306a36Sopenharmony_ci#include "clk.h" 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#define CLK_MGR_FREE_SHIFT 16 1362306a36Sopenharmony_ci#define CLK_MGR_FREE_MASK 0x7 1462306a36Sopenharmony_ci#define SWCTRLBTCLKSEN_SHIFT 8 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#define to_periph_clk(p) container_of(p, struct socfpga_periph_clk, hw.hw) 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_cistatic unsigned long n5x_clk_peri_c_clk_recalc_rate(struct clk_hw *hwclk, 1962306a36Sopenharmony_ci unsigned long parent_rate) 2062306a36Sopenharmony_ci{ 2162306a36Sopenharmony_ci struct socfpga_periph_clk *socfpgaclk = to_periph_clk(hwclk); 2262306a36Sopenharmony_ci unsigned long div; 2362306a36Sopenharmony_ci unsigned long shift = socfpgaclk->shift; 2462306a36Sopenharmony_ci u32 val; 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci val = readl(socfpgaclk->hw.reg); 2762306a36Sopenharmony_ci val &= (0x1f << shift); 2862306a36Sopenharmony_ci div = (val >> shift) + 1; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci return parent_rate / div; 3162306a36Sopenharmony_ci} 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistatic unsigned long clk_peri_c_clk_recalc_rate(struct clk_hw *hwclk, 3462306a36Sopenharmony_ci unsigned long parent_rate) 3562306a36Sopenharmony_ci{ 3662306a36Sopenharmony_ci struct socfpga_periph_clk *socfpgaclk = to_periph_clk(hwclk); 3762306a36Sopenharmony_ci unsigned long div = 1; 3862306a36Sopenharmony_ci u32 val; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci val = readl(socfpgaclk->hw.reg); 4162306a36Sopenharmony_ci val &= GENMASK(SWCTRLBTCLKSEN_SHIFT - 1, 0); 4262306a36Sopenharmony_ci parent_rate /= val; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci return parent_rate / div; 4562306a36Sopenharmony_ci} 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistatic unsigned long clk_peri_cnt_clk_recalc_rate(struct clk_hw *hwclk, 4862306a36Sopenharmony_ci unsigned long parent_rate) 4962306a36Sopenharmony_ci{ 5062306a36Sopenharmony_ci struct socfpga_periph_clk *socfpgaclk = to_periph_clk(hwclk); 5162306a36Sopenharmony_ci unsigned long div = 1; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci if (socfpgaclk->fixed_div) { 5462306a36Sopenharmony_ci div = socfpgaclk->fixed_div; 5562306a36Sopenharmony_ci } else { 5662306a36Sopenharmony_ci if (socfpgaclk->hw.reg) 5762306a36Sopenharmony_ci div = ((readl(socfpgaclk->hw.reg) & 0x7ff) + 1); 5862306a36Sopenharmony_ci } 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci return parent_rate / div; 6162306a36Sopenharmony_ci} 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistatic u8 clk_periclk_get_parent(struct clk_hw *hwclk) 6462306a36Sopenharmony_ci{ 6562306a36Sopenharmony_ci struct socfpga_periph_clk *socfpgaclk = to_periph_clk(hwclk); 6662306a36Sopenharmony_ci u32 clk_src, mask; 6762306a36Sopenharmony_ci u8 parent = 0; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci /* handle the bypass first */ 7062306a36Sopenharmony_ci if (socfpgaclk->bypass_reg) { 7162306a36Sopenharmony_ci mask = (0x1 << socfpgaclk->bypass_shift); 7262306a36Sopenharmony_ci parent = ((readl(socfpgaclk->bypass_reg) & mask) >> 7362306a36Sopenharmony_ci socfpgaclk->bypass_shift); 7462306a36Sopenharmony_ci if (parent) 7562306a36Sopenharmony_ci return parent; 7662306a36Sopenharmony_ci } 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci if (socfpgaclk->hw.reg) { 7962306a36Sopenharmony_ci clk_src = readl(socfpgaclk->hw.reg); 8062306a36Sopenharmony_ci parent = (clk_src >> CLK_MGR_FREE_SHIFT) & 8162306a36Sopenharmony_ci CLK_MGR_FREE_MASK; 8262306a36Sopenharmony_ci } 8362306a36Sopenharmony_ci return parent; 8462306a36Sopenharmony_ci} 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_cistatic const struct clk_ops n5x_peri_c_clk_ops = { 8762306a36Sopenharmony_ci .recalc_rate = n5x_clk_peri_c_clk_recalc_rate, 8862306a36Sopenharmony_ci .get_parent = clk_periclk_get_parent, 8962306a36Sopenharmony_ci}; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_cistatic const struct clk_ops peri_c_clk_ops = { 9262306a36Sopenharmony_ci .recalc_rate = clk_peri_c_clk_recalc_rate, 9362306a36Sopenharmony_ci .get_parent = clk_periclk_get_parent, 9462306a36Sopenharmony_ci}; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_cistatic const struct clk_ops peri_cnt_clk_ops = { 9762306a36Sopenharmony_ci .recalc_rate = clk_peri_cnt_clk_recalc_rate, 9862306a36Sopenharmony_ci .get_parent = clk_periclk_get_parent, 9962306a36Sopenharmony_ci}; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_cistruct clk_hw *s10_register_periph(const struct stratix10_perip_c_clock *clks, 10262306a36Sopenharmony_ci void __iomem *reg) 10362306a36Sopenharmony_ci{ 10462306a36Sopenharmony_ci struct clk_hw *hw_clk; 10562306a36Sopenharmony_ci struct socfpga_periph_clk *periph_clk; 10662306a36Sopenharmony_ci struct clk_init_data init; 10762306a36Sopenharmony_ci const char *name = clks->name; 10862306a36Sopenharmony_ci const char *parent_name = clks->parent_name; 10962306a36Sopenharmony_ci int ret; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci periph_clk = kzalloc(sizeof(*periph_clk), GFP_KERNEL); 11262306a36Sopenharmony_ci if (WARN_ON(!periph_clk)) 11362306a36Sopenharmony_ci return NULL; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci periph_clk->hw.reg = reg + clks->offset; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci init.name = name; 11862306a36Sopenharmony_ci init.ops = &peri_c_clk_ops; 11962306a36Sopenharmony_ci init.flags = clks->flags; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci init.num_parents = clks->num_parents; 12262306a36Sopenharmony_ci init.parent_names = parent_name ? &parent_name : NULL; 12362306a36Sopenharmony_ci if (init.parent_names == NULL) 12462306a36Sopenharmony_ci init.parent_data = clks->parent_data; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci periph_clk->hw.hw.init = &init; 12762306a36Sopenharmony_ci hw_clk = &periph_clk->hw.hw; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci ret = clk_hw_register(NULL, hw_clk); 13062306a36Sopenharmony_ci if (ret) { 13162306a36Sopenharmony_ci kfree(periph_clk); 13262306a36Sopenharmony_ci return ERR_PTR(ret); 13362306a36Sopenharmony_ci } 13462306a36Sopenharmony_ci return hw_clk; 13562306a36Sopenharmony_ci} 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_cistruct clk_hw *n5x_register_periph(const struct n5x_perip_c_clock *clks, 13862306a36Sopenharmony_ci void __iomem *regbase) 13962306a36Sopenharmony_ci{ 14062306a36Sopenharmony_ci struct clk_hw *hw_clk; 14162306a36Sopenharmony_ci struct socfpga_periph_clk *periph_clk; 14262306a36Sopenharmony_ci struct clk_init_data init; 14362306a36Sopenharmony_ci const char *name = clks->name; 14462306a36Sopenharmony_ci const char *parent_name = clks->parent_name; 14562306a36Sopenharmony_ci int ret; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci periph_clk = kzalloc(sizeof(*periph_clk), GFP_KERNEL); 14862306a36Sopenharmony_ci if (WARN_ON(!periph_clk)) 14962306a36Sopenharmony_ci return NULL; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci periph_clk->hw.reg = regbase + clks->offset; 15262306a36Sopenharmony_ci periph_clk->shift = clks->shift; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci init.name = name; 15562306a36Sopenharmony_ci init.ops = &n5x_peri_c_clk_ops; 15662306a36Sopenharmony_ci init.flags = clks->flags; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci init.num_parents = clks->num_parents; 15962306a36Sopenharmony_ci init.parent_names = parent_name ? &parent_name : NULL; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci periph_clk->hw.hw.init = &init; 16262306a36Sopenharmony_ci hw_clk = &periph_clk->hw.hw; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci ret = clk_hw_register(NULL, hw_clk); 16562306a36Sopenharmony_ci if (ret) { 16662306a36Sopenharmony_ci kfree(periph_clk); 16762306a36Sopenharmony_ci return ERR_PTR(ret); 16862306a36Sopenharmony_ci } 16962306a36Sopenharmony_ci return hw_clk; 17062306a36Sopenharmony_ci} 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_cistruct clk_hw *s10_register_cnt_periph(const struct stratix10_perip_cnt_clock *clks, 17362306a36Sopenharmony_ci void __iomem *regbase) 17462306a36Sopenharmony_ci{ 17562306a36Sopenharmony_ci struct clk_hw *hw_clk; 17662306a36Sopenharmony_ci struct socfpga_periph_clk *periph_clk; 17762306a36Sopenharmony_ci struct clk_init_data init; 17862306a36Sopenharmony_ci const char *name = clks->name; 17962306a36Sopenharmony_ci const char *parent_name = clks->parent_name; 18062306a36Sopenharmony_ci int ret; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci periph_clk = kzalloc(sizeof(*periph_clk), GFP_KERNEL); 18362306a36Sopenharmony_ci if (WARN_ON(!periph_clk)) 18462306a36Sopenharmony_ci return NULL; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci if (clks->offset) 18762306a36Sopenharmony_ci periph_clk->hw.reg = regbase + clks->offset; 18862306a36Sopenharmony_ci else 18962306a36Sopenharmony_ci periph_clk->hw.reg = NULL; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci if (clks->bypass_reg) 19262306a36Sopenharmony_ci periph_clk->bypass_reg = regbase + clks->bypass_reg; 19362306a36Sopenharmony_ci else 19462306a36Sopenharmony_ci periph_clk->bypass_reg = NULL; 19562306a36Sopenharmony_ci periph_clk->bypass_shift = clks->bypass_shift; 19662306a36Sopenharmony_ci periph_clk->fixed_div = clks->fixed_divider; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci init.name = name; 19962306a36Sopenharmony_ci init.ops = &peri_cnt_clk_ops; 20062306a36Sopenharmony_ci init.flags = clks->flags; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci init.num_parents = clks->num_parents; 20362306a36Sopenharmony_ci init.parent_names = parent_name ? &parent_name : NULL; 20462306a36Sopenharmony_ci if (init.parent_names == NULL) 20562306a36Sopenharmony_ci init.parent_data = clks->parent_data; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci periph_clk->hw.hw.init = &init; 20862306a36Sopenharmony_ci hw_clk = &periph_clk->hw.hw; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci ret = clk_hw_register(NULL, hw_clk); 21162306a36Sopenharmony_ci if (ret) { 21262306a36Sopenharmony_ci kfree(periph_clk); 21362306a36Sopenharmony_ci return ERR_PTR(ret); 21462306a36Sopenharmony_ci } 21562306a36Sopenharmony_ci return hw_clk; 21662306a36Sopenharmony_ci} 217