1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0+
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Marvell Armada 37xx SoC Time Base Generator clocks
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * Copyright (C) 2016 Marvell
6*4882a593Smuzhiyun *
7*4882a593Smuzhiyun * Gregory CLEMENT <gregory.clement@free-electrons.com>
8*4882a593Smuzhiyun */
9*4882a593Smuzhiyun
10*4882a593Smuzhiyun #include <linux/clk-provider.h>
11*4882a593Smuzhiyun #include <linux/clk.h>
12*4882a593Smuzhiyun #include <linux/io.h>
13*4882a593Smuzhiyun #include <linux/of.h>
14*4882a593Smuzhiyun #include <linux/of_address.h>
15*4882a593Smuzhiyun #include <linux/platform_device.h>
16*4882a593Smuzhiyun #include <linux/slab.h>
17*4882a593Smuzhiyun
18*4882a593Smuzhiyun #define NUM_TBG 4
19*4882a593Smuzhiyun
20*4882a593Smuzhiyun #define TBG_CTRL0 0x4
21*4882a593Smuzhiyun #define TBG_CTRL1 0x8
22*4882a593Smuzhiyun #define TBG_CTRL7 0x20
23*4882a593Smuzhiyun #define TBG_CTRL8 0x30
24*4882a593Smuzhiyun
25*4882a593Smuzhiyun #define TBG_DIV_MASK 0x1FF
26*4882a593Smuzhiyun
27*4882a593Smuzhiyun #define TBG_A_REFDIV 0
28*4882a593Smuzhiyun #define TBG_B_REFDIV 16
29*4882a593Smuzhiyun
30*4882a593Smuzhiyun #define TBG_A_FBDIV 2
31*4882a593Smuzhiyun #define TBG_B_FBDIV 18
32*4882a593Smuzhiyun
33*4882a593Smuzhiyun #define TBG_A_VCODIV_SE 0
34*4882a593Smuzhiyun #define TBG_B_VCODIV_SE 16
35*4882a593Smuzhiyun
36*4882a593Smuzhiyun #define TBG_A_VCODIV_DIFF 1
37*4882a593Smuzhiyun #define TBG_B_VCODIV_DIFF 17
38*4882a593Smuzhiyun
39*4882a593Smuzhiyun struct tbg_def {
40*4882a593Smuzhiyun char *name;
41*4882a593Smuzhiyun u32 refdiv_offset;
42*4882a593Smuzhiyun u32 fbdiv_offset;
43*4882a593Smuzhiyun u32 vcodiv_reg;
44*4882a593Smuzhiyun u32 vcodiv_offset;
45*4882a593Smuzhiyun };
46*4882a593Smuzhiyun
47*4882a593Smuzhiyun static const struct tbg_def tbg[NUM_TBG] = {
48*4882a593Smuzhiyun {"TBG-A-P", TBG_A_REFDIV, TBG_A_FBDIV, TBG_CTRL8, TBG_A_VCODIV_DIFF},
49*4882a593Smuzhiyun {"TBG-B-P", TBG_B_REFDIV, TBG_B_FBDIV, TBG_CTRL8, TBG_B_VCODIV_DIFF},
50*4882a593Smuzhiyun {"TBG-A-S", TBG_A_REFDIV, TBG_A_FBDIV, TBG_CTRL1, TBG_A_VCODIV_SE},
51*4882a593Smuzhiyun {"TBG-B-S", TBG_B_REFDIV, TBG_B_FBDIV, TBG_CTRL1, TBG_B_VCODIV_SE},
52*4882a593Smuzhiyun };
53*4882a593Smuzhiyun
tbg_get_mult(void __iomem * reg,const struct tbg_def * ptbg)54*4882a593Smuzhiyun static unsigned int tbg_get_mult(void __iomem *reg, const struct tbg_def *ptbg)
55*4882a593Smuzhiyun {
56*4882a593Smuzhiyun u32 val;
57*4882a593Smuzhiyun
58*4882a593Smuzhiyun val = readl(reg + TBG_CTRL0);
59*4882a593Smuzhiyun
60*4882a593Smuzhiyun return ((val >> ptbg->fbdiv_offset) & TBG_DIV_MASK) << 2;
61*4882a593Smuzhiyun }
62*4882a593Smuzhiyun
tbg_get_div(void __iomem * reg,const struct tbg_def * ptbg)63*4882a593Smuzhiyun static unsigned int tbg_get_div(void __iomem *reg, const struct tbg_def *ptbg)
64*4882a593Smuzhiyun {
65*4882a593Smuzhiyun u32 val;
66*4882a593Smuzhiyun unsigned int div;
67*4882a593Smuzhiyun
68*4882a593Smuzhiyun val = readl(reg + TBG_CTRL7);
69*4882a593Smuzhiyun
70*4882a593Smuzhiyun div = (val >> ptbg->refdiv_offset) & TBG_DIV_MASK;
71*4882a593Smuzhiyun if (div == 0)
72*4882a593Smuzhiyun div = 1;
73*4882a593Smuzhiyun val = readl(reg + ptbg->vcodiv_reg);
74*4882a593Smuzhiyun
75*4882a593Smuzhiyun div *= 1 << ((val >> ptbg->vcodiv_offset) & TBG_DIV_MASK);
76*4882a593Smuzhiyun
77*4882a593Smuzhiyun return div;
78*4882a593Smuzhiyun }
79*4882a593Smuzhiyun
80*4882a593Smuzhiyun
armada_3700_tbg_clock_probe(struct platform_device * pdev)81*4882a593Smuzhiyun static int armada_3700_tbg_clock_probe(struct platform_device *pdev)
82*4882a593Smuzhiyun {
83*4882a593Smuzhiyun struct device_node *np = pdev->dev.of_node;
84*4882a593Smuzhiyun struct clk_hw_onecell_data *hw_tbg_data;
85*4882a593Smuzhiyun struct device *dev = &pdev->dev;
86*4882a593Smuzhiyun const char *parent_name;
87*4882a593Smuzhiyun struct resource *res;
88*4882a593Smuzhiyun struct clk *parent;
89*4882a593Smuzhiyun void __iomem *reg;
90*4882a593Smuzhiyun int i, ret;
91*4882a593Smuzhiyun
92*4882a593Smuzhiyun hw_tbg_data = devm_kzalloc(&pdev->dev,
93*4882a593Smuzhiyun struct_size(hw_tbg_data, hws, NUM_TBG),
94*4882a593Smuzhiyun GFP_KERNEL);
95*4882a593Smuzhiyun if (!hw_tbg_data)
96*4882a593Smuzhiyun return -ENOMEM;
97*4882a593Smuzhiyun hw_tbg_data->num = NUM_TBG;
98*4882a593Smuzhiyun platform_set_drvdata(pdev, hw_tbg_data);
99*4882a593Smuzhiyun
100*4882a593Smuzhiyun parent = clk_get(dev, NULL);
101*4882a593Smuzhiyun if (IS_ERR(parent)) {
102*4882a593Smuzhiyun dev_err(dev, "Could get the clock parent\n");
103*4882a593Smuzhiyun return -EINVAL;
104*4882a593Smuzhiyun }
105*4882a593Smuzhiyun parent_name = __clk_get_name(parent);
106*4882a593Smuzhiyun clk_put(parent);
107*4882a593Smuzhiyun
108*4882a593Smuzhiyun res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
109*4882a593Smuzhiyun reg = devm_ioremap_resource(dev, res);
110*4882a593Smuzhiyun if (IS_ERR(reg))
111*4882a593Smuzhiyun return PTR_ERR(reg);
112*4882a593Smuzhiyun
113*4882a593Smuzhiyun for (i = 0; i < NUM_TBG; i++) {
114*4882a593Smuzhiyun const char *name;
115*4882a593Smuzhiyun unsigned int mult, div;
116*4882a593Smuzhiyun
117*4882a593Smuzhiyun name = tbg[i].name;
118*4882a593Smuzhiyun mult = tbg_get_mult(reg, &tbg[i]);
119*4882a593Smuzhiyun div = tbg_get_div(reg, &tbg[i]);
120*4882a593Smuzhiyun hw_tbg_data->hws[i] = clk_hw_register_fixed_factor(NULL, name,
121*4882a593Smuzhiyun parent_name, 0, mult, div);
122*4882a593Smuzhiyun if (IS_ERR(hw_tbg_data->hws[i]))
123*4882a593Smuzhiyun dev_err(dev, "Can't register TBG clock %s\n", name);
124*4882a593Smuzhiyun }
125*4882a593Smuzhiyun
126*4882a593Smuzhiyun ret = of_clk_add_hw_provider(np, of_clk_hw_onecell_get, hw_tbg_data);
127*4882a593Smuzhiyun
128*4882a593Smuzhiyun return ret;
129*4882a593Smuzhiyun }
130*4882a593Smuzhiyun
armada_3700_tbg_clock_remove(struct platform_device * pdev)131*4882a593Smuzhiyun static int armada_3700_tbg_clock_remove(struct platform_device *pdev)
132*4882a593Smuzhiyun {
133*4882a593Smuzhiyun int i;
134*4882a593Smuzhiyun struct clk_hw_onecell_data *hw_tbg_data = platform_get_drvdata(pdev);
135*4882a593Smuzhiyun
136*4882a593Smuzhiyun of_clk_del_provider(pdev->dev.of_node);
137*4882a593Smuzhiyun for (i = 0; i < hw_tbg_data->num; i++)
138*4882a593Smuzhiyun clk_hw_unregister_fixed_factor(hw_tbg_data->hws[i]);
139*4882a593Smuzhiyun
140*4882a593Smuzhiyun return 0;
141*4882a593Smuzhiyun }
142*4882a593Smuzhiyun
143*4882a593Smuzhiyun static const struct of_device_id armada_3700_tbg_clock_of_match[] = {
144*4882a593Smuzhiyun { .compatible = "marvell,armada-3700-tbg-clock", },
145*4882a593Smuzhiyun { }
146*4882a593Smuzhiyun };
147*4882a593Smuzhiyun
148*4882a593Smuzhiyun static struct platform_driver armada_3700_tbg_clock_driver = {
149*4882a593Smuzhiyun .probe = armada_3700_tbg_clock_probe,
150*4882a593Smuzhiyun .remove = armada_3700_tbg_clock_remove,
151*4882a593Smuzhiyun .driver = {
152*4882a593Smuzhiyun .name = "marvell-armada-3700-tbg-clock",
153*4882a593Smuzhiyun .of_match_table = armada_3700_tbg_clock_of_match,
154*4882a593Smuzhiyun },
155*4882a593Smuzhiyun };
156*4882a593Smuzhiyun
157*4882a593Smuzhiyun builtin_platform_driver(armada_3700_tbg_clock_driver);
158