1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Copyright (C) 2016-2017 Imagination Technologies 4 * Author: Paul Burton <paul.burton@mips.com> 5 */ 6 7 #define pr_fmt(fmt) "clk-boston: " fmt 8 9 #include <linux/clk-provider.h> 10 #include <linux/kernel.h> 11 #include <linux/of.h> 12 #include <linux/regmap.h> 13 #include <linux/slab.h> 14 #include <linux/mfd/syscon.h> 15 16 #include <dt-bindings/clock/boston-clock.h> 17 18 #define BOSTON_PLAT_MMCMDIV 0x30 19 # define BOSTON_PLAT_MMCMDIV_CLK0DIV (0xff << 0) 20 # define BOSTON_PLAT_MMCMDIV_INPUT (0xff << 8) 21 # define BOSTON_PLAT_MMCMDIV_MUL (0xff << 16) 22 # define BOSTON_PLAT_MMCMDIV_CLK1DIV (0xff << 24) 23 24 #define BOSTON_CLK_COUNT 3 25 26 static u32 ext_field(u32 val, u32 mask) 27 { 28 return (val & mask) >> (ffs(mask) - 1); 29 } 30 31 static void __init clk_boston_setup(struct device_node *np) 32 { 33 unsigned long in_freq, cpu_freq, sys_freq; 34 uint mmcmdiv, mul, cpu_div, sys_div; 35 struct clk_hw_onecell_data *onecell; 36 struct regmap *regmap; 37 struct clk_hw *hw; 38 int err; 39 40 regmap = syscon_node_to_regmap(np->parent); 41 if (IS_ERR(regmap)) { 42 pr_err("failed to find regmap\n"); 43 return; 44 } 45 46 err = regmap_read(regmap, BOSTON_PLAT_MMCMDIV, &mmcmdiv); 47 if (err) { 48 pr_err("failed to read mmcm_div register: %d\n", err); 49 return; 50 } 51 52 in_freq = ext_field(mmcmdiv, BOSTON_PLAT_MMCMDIV_INPUT) * 1000000; 53 mul = ext_field(mmcmdiv, BOSTON_PLAT_MMCMDIV_MUL); 54 55 sys_div = ext_field(mmcmdiv, BOSTON_PLAT_MMCMDIV_CLK0DIV); 56 sys_freq = mult_frac(in_freq, mul, sys_div); 57 58 cpu_div = ext_field(mmcmdiv, BOSTON_PLAT_MMCMDIV_CLK1DIV); 59 cpu_freq = mult_frac(in_freq, mul, cpu_div); 60 61 onecell = kzalloc(sizeof(*onecell) + 62 (BOSTON_CLK_COUNT * sizeof(struct clk_hw *)), 63 GFP_KERNEL); 64 if (!onecell) 65 return; 66 67 onecell->num = BOSTON_CLK_COUNT; 68 69 hw = clk_hw_register_fixed_rate(NULL, "input", NULL, 0, in_freq); 70 if (IS_ERR(hw)) { 71 pr_err("failed to register input clock: %ld\n", PTR_ERR(hw)); 72 goto fail_input; 73 } 74 onecell->hws[BOSTON_CLK_INPUT] = hw; 75 76 hw = clk_hw_register_fixed_rate(NULL, "sys", "input", 0, sys_freq); 77 if (IS_ERR(hw)) { 78 pr_err("failed to register sys clock: %ld\n", PTR_ERR(hw)); 79 goto fail_sys; 80 } 81 onecell->hws[BOSTON_CLK_SYS] = hw; 82 83 hw = clk_hw_register_fixed_rate(NULL, "cpu", "input", 0, cpu_freq); 84 if (IS_ERR(hw)) { 85 pr_err("failed to register cpu clock: %ld\n", PTR_ERR(hw)); 86 goto fail_cpu; 87 } 88 onecell->hws[BOSTON_CLK_CPU] = hw; 89 90 err = of_clk_add_hw_provider(np, of_clk_hw_onecell_get, onecell); 91 if (err) { 92 pr_err("failed to add DT provider: %d\n", err); 93 goto fail_clk_add; 94 } 95 96 return; 97 98 fail_clk_add: 99 clk_hw_unregister_fixed_rate(onecell->hws[BOSTON_CLK_CPU]); 100 fail_cpu: 101 clk_hw_unregister_fixed_rate(onecell->hws[BOSTON_CLK_SYS]); 102 fail_sys: 103 clk_hw_unregister_fixed_rate(onecell->hws[BOSTON_CLK_INPUT]); 104 fail_input: 105 kfree(onecell); 106 } 107 108 /* 109 * Use CLK_OF_DECLARE so that this driver is probed early enough to provide the 110 * CPU frequency for use with the GIC or cop0 counters/timers. 111 */ 112 CLK_OF_DECLARE(clk_boston, "img,boston-clock", clk_boston_setup); 113