1 // SPDX-License-Identifier: GPL-2.0+
3 * Marvell Armada 37xx SoC Time Base Generator clocks
5 * Copyright (C) 2016 Marvell
7 * Gregory CLEMENT <gregory.clement@free-electrons.com>
10 #include <linux/clk-provider.h>
11 #include <linux/clk.h>
14 #include <linux/of_address.h>
15 #include <linux/platform_device.h>
16 #include <linux/slab.h>
22 #define TBG_CTRL7 0x20
23 #define TBG_CTRL8 0x30
25 #define TBG_DIV_MASK 0x1FF
27 #define TBG_A_REFDIV 0
28 #define TBG_B_REFDIV 16
31 #define TBG_B_FBDIV 18
33 #define TBG_A_VCODIV_SE 0
34 #define TBG_B_VCODIV_SE 16
36 #define TBG_A_VCODIV_DIFF 1
37 #define TBG_B_VCODIV_DIFF 17
47 static const struct tbg_def tbg
[NUM_TBG
] = {
48 {"TBG-A-P", TBG_A_REFDIV
, TBG_A_FBDIV
, TBG_CTRL8
, TBG_A_VCODIV_DIFF
},
49 {"TBG-B-P", TBG_B_REFDIV
, TBG_B_FBDIV
, TBG_CTRL8
, TBG_B_VCODIV_DIFF
},
50 {"TBG-A-S", TBG_A_REFDIV
, TBG_A_FBDIV
, TBG_CTRL1
, TBG_A_VCODIV_SE
},
51 {"TBG-B-S", TBG_B_REFDIV
, TBG_B_FBDIV
, TBG_CTRL1
, TBG_B_VCODIV_SE
},
54 static unsigned int tbg_get_mult(void __iomem
*reg
, const struct tbg_def
*ptbg
)
58 val
= readl(reg
+ TBG_CTRL0
);
60 return ((val
>> ptbg
->fbdiv_offset
) & TBG_DIV_MASK
) << 2;
63 static unsigned int tbg_get_div(void __iomem
*reg
, const struct tbg_def
*ptbg
)
68 val
= readl(reg
+ TBG_CTRL7
);
70 div
= (val
>> ptbg
->refdiv_offset
) & TBG_DIV_MASK
;
73 val
= readl(reg
+ ptbg
->vcodiv_reg
);
75 div
*= 1 << ((val
>> ptbg
->vcodiv_offset
) & TBG_DIV_MASK
);
81 static int armada_3700_tbg_clock_probe(struct platform_device
*pdev
)
83 struct device_node
*np
= pdev
->dev
.of_node
;
84 struct clk_hw_onecell_data
*hw_tbg_data
;
85 struct device
*dev
= &pdev
->dev
;
86 const char *parent_name
;
92 hw_tbg_data
= devm_kzalloc(&pdev
->dev
,
93 struct_size(hw_tbg_data
, hws
, NUM_TBG
),
97 hw_tbg_data
->num
= NUM_TBG
;
98 platform_set_drvdata(pdev
, hw_tbg_data
);
100 parent
= clk_get(dev
, NULL
);
101 if (IS_ERR(parent
)) {
102 dev_err(dev
, "Could get the clock parent\n");
105 parent_name
= __clk_get_name(parent
);
108 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
109 reg
= devm_ioremap_resource(dev
, res
);
113 for (i
= 0; i
< NUM_TBG
; i
++) {
115 unsigned int mult
, div
;
118 mult
= tbg_get_mult(reg
, &tbg
[i
]);
119 div
= tbg_get_div(reg
, &tbg
[i
]);
120 hw_tbg_data
->hws
[i
] = clk_hw_register_fixed_factor(NULL
, name
,
121 parent_name
, 0, mult
, div
);
122 if (IS_ERR(hw_tbg_data
->hws
[i
]))
123 dev_err(dev
, "Can't register TBG clock %s\n", name
);
126 ret
= of_clk_add_hw_provider(np
, of_clk_hw_onecell_get
, hw_tbg_data
);
131 static int armada_3700_tbg_clock_remove(struct platform_device
*pdev
)
134 struct clk_hw_onecell_data
*hw_tbg_data
= platform_get_drvdata(pdev
);
136 of_clk_del_provider(pdev
->dev
.of_node
);
137 for (i
= 0; i
< hw_tbg_data
->num
; i
++)
138 clk_hw_unregister_fixed_factor(hw_tbg_data
->hws
[i
]);
143 static const struct of_device_id armada_3700_tbg_clock_of_match
[] = {
144 { .compatible
= "marvell,armada-3700-tbg-clock", },
148 static struct platform_driver armada_3700_tbg_clock_driver
= {
149 .probe
= armada_3700_tbg_clock_probe
,
150 .remove
= armada_3700_tbg_clock_remove
,
152 .name
= "marvell-armada-3700-tbg-clock",
153 .of_match_table
= armada_3700_tbg_clock_of_match
,
157 builtin_platform_driver(armada_3700_tbg_clock_driver
);