1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Copyright (C) 2016-2017 Imagination Technologies
4 * Author: Paul Burton <paul.burton@mips.com>
7 #define pr_fmt(fmt) "clk-boston: " fmt
9 #include <linux/clk-provider.h>
10 #include <linux/kernel.h>
12 #include <linux/regmap.h>
13 #include <linux/slab.h>
14 #include <linux/mfd/syscon.h>
16 #include <dt-bindings/clock/boston-clock.h>
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)
24 #define BOSTON_CLK_COUNT 3
26 static u32
ext_field(u32 val
, u32 mask
)
28 return (val
& mask
) >> (ffs(mask
) - 1);
31 static void __init
clk_boston_setup(struct device_node
*np
)
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
;
40 regmap
= syscon_node_to_regmap(np
->parent
);
42 pr_err("failed to find regmap\n");
46 err
= regmap_read(regmap
, BOSTON_PLAT_MMCMDIV
, &mmcmdiv
);
48 pr_err("failed to read mmcm_div register: %d\n", err
);
52 in_freq
= ext_field(mmcmdiv
, BOSTON_PLAT_MMCMDIV_INPUT
) * 1000000;
53 mul
= ext_field(mmcmdiv
, BOSTON_PLAT_MMCMDIV_MUL
);
55 sys_div
= ext_field(mmcmdiv
, BOSTON_PLAT_MMCMDIV_CLK0DIV
);
56 sys_freq
= mult_frac(in_freq
, mul
, sys_div
);
58 cpu_div
= ext_field(mmcmdiv
, BOSTON_PLAT_MMCMDIV_CLK1DIV
);
59 cpu_freq
= mult_frac(in_freq
, mul
, cpu_div
);
61 onecell
= kzalloc(struct_size(onecell
, hws
, BOSTON_CLK_COUNT
),
66 onecell
->num
= BOSTON_CLK_COUNT
;
68 hw
= clk_hw_register_fixed_rate(NULL
, "input", NULL
, 0, in_freq
);
70 pr_err("failed to register input clock: %ld\n", PTR_ERR(hw
));
73 onecell
->hws
[BOSTON_CLK_INPUT
] = hw
;
75 hw
= clk_hw_register_fixed_rate(NULL
, "sys", "input", 0, sys_freq
);
77 pr_err("failed to register sys clock: %ld\n", PTR_ERR(hw
));
80 onecell
->hws
[BOSTON_CLK_SYS
] = hw
;
82 hw
= clk_hw_register_fixed_rate(NULL
, "cpu", "input", 0, cpu_freq
);
84 pr_err("failed to register cpu clock: %ld\n", PTR_ERR(hw
));
87 onecell
->hws
[BOSTON_CLK_CPU
] = hw
;
89 err
= of_clk_add_hw_provider(np
, of_clk_hw_onecell_get
, onecell
);
91 pr_err("failed to add DT provider: %d\n", err
);
98 clk_hw_unregister_fixed_rate(onecell
->hws
[BOSTON_CLK_CPU
]);
100 clk_hw_unregister_fixed_rate(onecell
->hws
[BOSTON_CLK_SYS
]);
102 clk_hw_unregister_fixed_rate(onecell
->hws
[BOSTON_CLK_INPUT
]);
108 * Use CLK_OF_DECLARE so that this driver is probed early enough to provide the
109 * CPU frequency for use with the GIC or cop0 counters/timers.
111 CLK_OF_DECLARE(clk_boston
, "img,boston-clock", clk_boston_setup
);