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