2 * Copyright (C) 2016-2017 Imagination Technologies
3 * Author: Paul Burton <paul.burton@mips.com>
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version.
11 #define pr_fmt(fmt) "clk-boston: " fmt
13 #include <linux/clk-provider.h>
14 #include <linux/kernel.h>
16 #include <linux/regmap.h>
17 #include <linux/slab.h>
18 #include <linux/mfd/syscon.h>
20 #include <dt-bindings/clock/boston-clock.h>
22 #define BOSTON_PLAT_MMCMDIV 0x30
23 # define BOSTON_PLAT_MMCMDIV_CLK0DIV (0xff << 0)
24 # define BOSTON_PLAT_MMCMDIV_INPUT (0xff << 8)
25 # define BOSTON_PLAT_MMCMDIV_MUL (0xff << 16)
26 # define BOSTON_PLAT_MMCMDIV_CLK1DIV (0xff << 24)
28 #define BOSTON_CLK_COUNT 3
30 static u32
ext_field(u32 val
, u32 mask
)
32 return (val
& mask
) >> (ffs(mask
) - 1);
35 static void __init
clk_boston_setup(struct device_node
*np
)
37 unsigned long in_freq
, cpu_freq
, sys_freq
;
38 uint mmcmdiv
, mul
, cpu_div
, sys_div
;
39 struct clk_hw_onecell_data
*onecell
;
40 struct regmap
*regmap
;
44 regmap
= syscon_node_to_regmap(np
->parent
);
46 pr_err("failed to find regmap\n");
50 err
= regmap_read(regmap
, BOSTON_PLAT_MMCMDIV
, &mmcmdiv
);
52 pr_err("failed to read mmcm_div register: %d\n", err
);
56 in_freq
= ext_field(mmcmdiv
, BOSTON_PLAT_MMCMDIV_INPUT
) * 1000000;
57 mul
= ext_field(mmcmdiv
, BOSTON_PLAT_MMCMDIV_MUL
);
59 sys_div
= ext_field(mmcmdiv
, BOSTON_PLAT_MMCMDIV_CLK0DIV
);
60 sys_freq
= mult_frac(in_freq
, mul
, sys_div
);
62 cpu_div
= ext_field(mmcmdiv
, BOSTON_PLAT_MMCMDIV_CLK1DIV
);
63 cpu_freq
= mult_frac(in_freq
, mul
, cpu_div
);
65 onecell
= kzalloc(sizeof(*onecell
) +
66 (BOSTON_CLK_COUNT
* sizeof(struct clk_hw
*)),
71 onecell
->num
= BOSTON_CLK_COUNT
;
73 hw
= clk_hw_register_fixed_rate(NULL
, "input", NULL
, 0, in_freq
);
75 pr_err("failed to register input clock: %ld\n", PTR_ERR(hw
));
78 onecell
->hws
[BOSTON_CLK_INPUT
] = hw
;
80 hw
= clk_hw_register_fixed_rate(NULL
, "sys", "input", 0, sys_freq
);
82 pr_err("failed to register sys clock: %ld\n", PTR_ERR(hw
));
85 onecell
->hws
[BOSTON_CLK_SYS
] = hw
;
87 hw
= clk_hw_register_fixed_rate(NULL
, "cpu", "input", 0, cpu_freq
);
89 pr_err("failed to register cpu clock: %ld\n", PTR_ERR(hw
));
92 onecell
->hws
[BOSTON_CLK_CPU
] = hw
;
94 err
= of_clk_add_hw_provider(np
, of_clk_hw_onecell_get
, onecell
);
96 pr_err("failed to add DT provider: %d\n", err
);
103 clk_hw_unregister_fixed_rate(onecell
->hws
[BOSTON_CLK_CPU
]);
105 clk_hw_unregister_fixed_rate(onecell
->hws
[BOSTON_CLK_SYS
]);
107 clk_hw_unregister_fixed_rate(onecell
->hws
[BOSTON_CLK_INPUT
]);
113 * Use CLK_OF_DECLARE so that this driver is probed early enough to provide the
114 * CPU frequency for use with the GIC or cop0 counters/timers.
116 CLK_OF_DECLARE(clk_boston
, "img,boston-clock", clk_boston_setup
);