162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2015 Altera Corporation. All rights reserved 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci#include <linux/slab.h> 662306a36Sopenharmony_ci#include <linux/clk-provider.h> 762306a36Sopenharmony_ci#include <linux/io.h> 862306a36Sopenharmony_ci#include <linux/of.h> 962306a36Sopenharmony_ci 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 1562306a36Sopenharmony_ci#define SOCFPGA_MPU_FREE_CLK "mpu_free_clk" 1662306a36Sopenharmony_ci#define SOCFPGA_NOC_FREE_CLK "noc_free_clk" 1762306a36Sopenharmony_ci#define SOCFPGA_SDMMC_FREE_CLK "sdmmc_free_clk" 1862306a36Sopenharmony_ci#define to_socfpga_periph_clk(p) container_of(p, struct socfpga_periph_clk, hw.hw) 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_cistatic unsigned long clk_periclk_recalc_rate(struct clk_hw *hwclk, 2162306a36Sopenharmony_ci unsigned long parent_rate) 2262306a36Sopenharmony_ci{ 2362306a36Sopenharmony_ci struct socfpga_periph_clk *socfpgaclk = to_socfpga_periph_clk(hwclk); 2462306a36Sopenharmony_ci u32 div; 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci if (socfpgaclk->fixed_div) { 2762306a36Sopenharmony_ci div = socfpgaclk->fixed_div; 2862306a36Sopenharmony_ci } else if (socfpgaclk->div_reg) { 2962306a36Sopenharmony_ci div = readl(socfpgaclk->div_reg) >> socfpgaclk->shift; 3062306a36Sopenharmony_ci div &= GENMASK(socfpgaclk->width - 1, 0); 3162306a36Sopenharmony_ci div += 1; 3262306a36Sopenharmony_ci } else { 3362306a36Sopenharmony_ci div = ((readl(socfpgaclk->hw.reg) & 0x7ff) + 1); 3462306a36Sopenharmony_ci } 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci return parent_rate / div; 3762306a36Sopenharmony_ci} 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistatic u8 clk_periclk_get_parent(struct clk_hw *hwclk) 4062306a36Sopenharmony_ci{ 4162306a36Sopenharmony_ci struct socfpga_periph_clk *socfpgaclk = to_socfpga_periph_clk(hwclk); 4262306a36Sopenharmony_ci u32 clk_src; 4362306a36Sopenharmony_ci const char *name = clk_hw_get_name(hwclk); 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci clk_src = readl(socfpgaclk->hw.reg); 4662306a36Sopenharmony_ci if (streq(name, SOCFPGA_MPU_FREE_CLK) || 4762306a36Sopenharmony_ci streq(name, SOCFPGA_NOC_FREE_CLK) || 4862306a36Sopenharmony_ci streq(name, SOCFPGA_SDMMC_FREE_CLK)) 4962306a36Sopenharmony_ci return (clk_src >> CLK_MGR_FREE_SHIFT) & 5062306a36Sopenharmony_ci CLK_MGR_FREE_MASK; 5162306a36Sopenharmony_ci else 5262306a36Sopenharmony_ci return 0; 5362306a36Sopenharmony_ci} 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistatic const struct clk_ops periclk_ops = { 5662306a36Sopenharmony_ci .recalc_rate = clk_periclk_recalc_rate, 5762306a36Sopenharmony_ci .get_parent = clk_periclk_get_parent, 5862306a36Sopenharmony_ci}; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistatic void __init __socfpga_periph_init(struct device_node *node, 6162306a36Sopenharmony_ci const struct clk_ops *ops) 6262306a36Sopenharmony_ci{ 6362306a36Sopenharmony_ci u32 reg; 6462306a36Sopenharmony_ci struct clk_hw *hw_clk; 6562306a36Sopenharmony_ci struct socfpga_periph_clk *periph_clk; 6662306a36Sopenharmony_ci const char *clk_name = node->name; 6762306a36Sopenharmony_ci const char *parent_name[SOCFPGA_MAX_PARENTS]; 6862306a36Sopenharmony_ci struct clk_init_data init; 6962306a36Sopenharmony_ci int rc; 7062306a36Sopenharmony_ci u32 fixed_div; 7162306a36Sopenharmony_ci u32 div_reg[3]; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci of_property_read_u32(node, "reg", ®); 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci periph_clk = kzalloc(sizeof(*periph_clk), GFP_KERNEL); 7662306a36Sopenharmony_ci if (WARN_ON(!periph_clk)) 7762306a36Sopenharmony_ci return; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci periph_clk->hw.reg = clk_mgr_a10_base_addr + reg; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci rc = of_property_read_u32_array(node, "div-reg", div_reg, 3); 8262306a36Sopenharmony_ci if (!rc) { 8362306a36Sopenharmony_ci periph_clk->div_reg = clk_mgr_a10_base_addr + div_reg[0]; 8462306a36Sopenharmony_ci periph_clk->shift = div_reg[1]; 8562306a36Sopenharmony_ci periph_clk->width = div_reg[2]; 8662306a36Sopenharmony_ci } else { 8762306a36Sopenharmony_ci periph_clk->div_reg = NULL; 8862306a36Sopenharmony_ci } 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci rc = of_property_read_u32(node, "fixed-divider", &fixed_div); 9162306a36Sopenharmony_ci if (rc) 9262306a36Sopenharmony_ci periph_clk->fixed_div = 0; 9362306a36Sopenharmony_ci else 9462306a36Sopenharmony_ci periph_clk->fixed_div = fixed_div; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci of_property_read_string(node, "clock-output-names", &clk_name); 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci init.name = clk_name; 9962306a36Sopenharmony_ci init.ops = ops; 10062306a36Sopenharmony_ci init.flags = 0; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci init.num_parents = of_clk_parent_fill(node, parent_name, SOCFPGA_MAX_PARENTS); 10362306a36Sopenharmony_ci init.parent_names = parent_name; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci periph_clk->hw.hw.init = &init; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci hw_clk = &periph_clk->hw.hw; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci rc = clk_hw_register(NULL, hw_clk); 11062306a36Sopenharmony_ci if (rc) { 11162306a36Sopenharmony_ci pr_err("Could not register clock:%s\n", clk_name); 11262306a36Sopenharmony_ci goto err_clk_hw_register; 11362306a36Sopenharmony_ci } 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci rc = of_clk_add_hw_provider(node, of_clk_hw_simple_get, hw_clk); 11662306a36Sopenharmony_ci if (rc) { 11762306a36Sopenharmony_ci pr_err("Could not register clock provider for node:%s\n", 11862306a36Sopenharmony_ci clk_name); 11962306a36Sopenharmony_ci goto err_of_clk_add_hw_provider; 12062306a36Sopenharmony_ci } 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci return; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_cierr_of_clk_add_hw_provider: 12562306a36Sopenharmony_ci clk_hw_unregister(hw_clk); 12662306a36Sopenharmony_cierr_clk_hw_register: 12762306a36Sopenharmony_ci kfree(periph_clk); 12862306a36Sopenharmony_ci} 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_civoid __init socfpga_a10_periph_init(struct device_node *node) 13162306a36Sopenharmony_ci{ 13262306a36Sopenharmony_ci __socfpga_periph_init(node, &periclk_ops); 13362306a36Sopenharmony_ci} 134