2 * Marvell Armada 37xx SoC Time Base Generator clocks
4 * Copyright (C) 2016 Marvell
6 * Gregory CLEMENT <gregory.clement@free-electrons.com>
8 * This file is licensed under the terms of the GNU General Public
9 * License version 2 or later. This program is licensed "as is"
10 * without any warranty of any kind, whether express or implied.
13 #include <linux/clk-provider.h>
14 #include <linux/clk.h>
16 #include <linux/of_address.h>
17 #include <linux/platform_device.h>
18 #include <linux/slab.h>
24 #define TBG_CTRL7 0x20
25 #define TBG_CTRL8 0x30
27 #define TBG_DIV_MASK 0x1FF
29 #define TBG_A_REFDIV 0
30 #define TBG_B_REFDIV 16
33 #define TBG_B_FBDIV 18
35 #define TBG_A_VCODIV_SE 0
36 #define TBG_B_VCODIV_SE 16
38 #define TBG_A_VCODIV_DIFF 1
39 #define TBG_B_VCODIV_DIFF 17
49 static const struct tbg_def tbg
[NUM_TBG
] = {
50 {"TBG-A-P", TBG_A_REFDIV
, TBG_A_FBDIV
, TBG_CTRL8
, TBG_A_VCODIV_DIFF
},
51 {"TBG-B-P", TBG_B_REFDIV
, TBG_B_FBDIV
, TBG_CTRL8
, TBG_B_VCODIV_DIFF
},
52 {"TBG-A-S", TBG_A_REFDIV
, TBG_A_FBDIV
, TBG_CTRL1
, TBG_A_VCODIV_SE
},
53 {"TBG-B-S", TBG_B_REFDIV
, TBG_B_FBDIV
, TBG_CTRL1
, TBG_B_VCODIV_SE
},
56 static unsigned int tbg_get_mult(void __iomem
*reg
, const struct tbg_def
*ptbg
)
60 val
= readl(reg
+ TBG_CTRL0
);
62 return ((val
>> ptbg
->fbdiv_offset
) & TBG_DIV_MASK
) << 2;
65 static unsigned int tbg_get_div(void __iomem
*reg
, const struct tbg_def
*ptbg
)
70 val
= readl(reg
+ TBG_CTRL7
);
72 div
= (val
>> ptbg
->refdiv_offset
) & TBG_DIV_MASK
;
75 val
= readl(reg
+ ptbg
->vcodiv_reg
);
77 div
*= 1 << ((val
>> ptbg
->vcodiv_offset
) & TBG_DIV_MASK
);
83 static int armada_3700_tbg_clock_probe(struct platform_device
*pdev
)
85 struct device_node
*np
= pdev
->dev
.of_node
;
86 struct clk_hw_onecell_data
*hw_tbg_data
;
87 struct device
*dev
= &pdev
->dev
;
88 const char *parent_name
;
94 hw_tbg_data
= devm_kzalloc(&pdev
->dev
, sizeof(*hw_tbg_data
)
95 + sizeof(*hw_tbg_data
->hws
) * NUM_TBG
,
99 hw_tbg_data
->num
= NUM_TBG
;
100 platform_set_drvdata(pdev
, hw_tbg_data
);
102 parent
= devm_clk_get(dev
, NULL
);
103 if (IS_ERR(parent
)) {
104 dev_err(dev
, "Could get the clock parent\n");
107 parent_name
= __clk_get_name(parent
);
109 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
110 reg
= devm_ioremap_resource(dev
, res
);
114 for (i
= 0; i
< NUM_TBG
; i
++) {
116 unsigned int mult
, div
;
119 mult
= tbg_get_mult(reg
, &tbg
[i
]);
120 div
= tbg_get_div(reg
, &tbg
[i
]);
121 hw_tbg_data
->hws
[i
] = clk_hw_register_fixed_factor(NULL
, name
,
122 parent_name
, 0, mult
, div
);
123 if (IS_ERR(hw_tbg_data
->hws
[i
]))
124 dev_err(dev
, "Can't register TBG clock %s\n", name
);
127 ret
= of_clk_add_hw_provider(np
, of_clk_hw_onecell_get
, hw_tbg_data
);
132 static int armada_3700_tbg_clock_remove(struct platform_device
*pdev
)
135 struct clk_hw_onecell_data
*hw_tbg_data
= platform_get_drvdata(pdev
);
137 of_clk_del_provider(pdev
->dev
.of_node
);
138 for (i
= 0; i
< hw_tbg_data
->num
; i
++)
139 clk_hw_unregister_fixed_factor(hw_tbg_data
->hws
[i
]);
144 static const struct of_device_id armada_3700_tbg_clock_of_match
[] = {
145 { .compatible
= "marvell,armada-3700-tbg-clock", },
149 static struct platform_driver armada_3700_tbg_clock_driver
= {
150 .probe
= armada_3700_tbg_clock_probe
,
151 .remove
= armada_3700_tbg_clock_remove
,
153 .name
= "marvell-armada-3700-tbg-clock",
154 .of_match_table
= armada_3700_tbg_clock_of_match
,
158 builtin_platform_driver(armada_3700_tbg_clock_driver
);