Skip to content

Commit c8a76ca

Browse files
Boris BREZILLONmripard
authored andcommitted
clk: sunxi: add PRCM (Power/Reset/Clock Management) clks support
The PRCM (Power/Reset/Clock Management) unit provides several clock devices: - AR100 clk: used to clock the Power Management co-processor - AHB0 clk: used to clock the AHB0 bus - APB0 clk and gates: used to clk peripherals connected to the APB0 bus Add support for these clks in a separate driver so that they can be probed as platform devices instead of registered during early init. This is needed to be able to probe PRCM MFD subdevices. Signed-off-by: Boris BREZILLON <[email protected]> Acked-by: Maxime Ripard <[email protected]> Signed-off-by: Emilio López <[email protected]>
1 parent efb3184 commit c8a76ca

File tree

4 files changed

+411
-0
lines changed

4 files changed

+411
-0
lines changed

drivers/clk/sunxi/Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,5 @@
55
obj-y += clk-sunxi.o clk-factors.o
66
obj-y += clk-a10-hosc.o
77
obj-y += clk-a20-gmac.o
8+
9+
obj-$(CONFIG_MFD_SUN6I_PRCM) += clk-sun6i-ar100.o clk-sun6i-apb0.o clk-sun6i-apb0-gates.o
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
/*
2+
* Copyright (C) 2014 Free Electrons
3+
*
4+
* License Terms: GNU General Public License v2
5+
* Author: Boris BREZILLON <[email protected]>
6+
*
7+
* Allwinner A31 APB0 clock gates driver
8+
*
9+
*/
10+
11+
#include <linux/clk-provider.h>
12+
#include <linux/module.h>
13+
#include <linux/of.h>
14+
#include <linux/platform_device.h>
15+
16+
#define SUN6I_APB0_GATES_MAX_SIZE 32
17+
18+
static int sun6i_a31_apb0_gates_clk_probe(struct platform_device *pdev)
19+
{
20+
struct device_node *np = pdev->dev.of_node;
21+
struct clk_onecell_data *clk_data;
22+
const char *clk_parent;
23+
const char *clk_name;
24+
struct resource *r;
25+
void __iomem *reg;
26+
int gate_id;
27+
int ngates;
28+
int i;
29+
30+
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
31+
reg = devm_ioremap_resource(&pdev->dev, r);
32+
if (!reg)
33+
return PTR_ERR(reg);
34+
35+
clk_parent = of_clk_get_parent_name(np, 0);
36+
if (!clk_parent)
37+
return -EINVAL;
38+
39+
ngates = of_property_count_strings(np, "clock-output-names");
40+
if (ngates < 0)
41+
return ngates;
42+
43+
if (!ngates || ngates > SUN6I_APB0_GATES_MAX_SIZE)
44+
return -EINVAL;
45+
46+
clk_data = devm_kzalloc(&pdev->dev, sizeof(struct clk_onecell_data),
47+
GFP_KERNEL);
48+
if (!clk_data)
49+
return -ENOMEM;
50+
51+
clk_data->clks = devm_kzalloc(&pdev->dev,
52+
SUN6I_APB0_GATES_MAX_SIZE *
53+
sizeof(struct clk *),
54+
GFP_KERNEL);
55+
if (!clk_data->clks)
56+
return -ENOMEM;
57+
58+
for (i = 0; i < ngates; i++) {
59+
of_property_read_string_index(np, "clock-output-names",
60+
i, &clk_name);
61+
62+
gate_id = i;
63+
of_property_read_u32_index(np, "clock-indices", i, &gate_id);
64+
65+
WARN_ON(gate_id >= SUN6I_APB0_GATES_MAX_SIZE);
66+
if (gate_id >= SUN6I_APB0_GATES_MAX_SIZE)
67+
continue;
68+
69+
clk_data->clks[gate_id] = clk_register_gate(&pdev->dev,
70+
clk_name,
71+
clk_parent, 0,
72+
reg, gate_id,
73+
0, NULL);
74+
WARN_ON(IS_ERR(clk_data->clks[gate_id]));
75+
}
76+
77+
clk_data->clk_num = ngates;
78+
79+
return of_clk_add_provider(np, of_clk_src_onecell_get, clk_data);
80+
}
81+
82+
const struct of_device_id sun6i_a31_apb0_gates_clk_dt_ids[] = {
83+
{ .compatible = "allwinner,sun6i-a31-apb0-gates-clk" },
84+
{ /* sentinel */ }
85+
};
86+
87+
static struct platform_driver sun6i_a31_apb0_gates_clk_driver = {
88+
.driver = {
89+
.name = "sun6i-a31-apb0-gates-clk",
90+
.owner = THIS_MODULE,
91+
.of_match_table = sun6i_a31_apb0_gates_clk_dt_ids,
92+
},
93+
.probe = sun6i_a31_apb0_gates_clk_probe,
94+
};
95+
module_platform_driver(sun6i_a31_apb0_gates_clk_driver);
96+
97+
MODULE_AUTHOR("Boris BREZILLON <[email protected]>");
98+
MODULE_DESCRIPTION("Allwinner A31 APB0 gate clocks driver");
99+
MODULE_LICENSE("GPL v2");

drivers/clk/sunxi/clk-sun6i-apb0.c

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/*
2+
* Copyright (C) 2014 Free Electrons
3+
*
4+
* License Terms: GNU General Public License v2
5+
* Author: Boris BREZILLON <[email protected]>
6+
*
7+
* Allwinner A31 APB0 clock driver
8+
*
9+
*/
10+
11+
#include <linux/clk-provider.h>
12+
#include <linux/module.h>
13+
#include <linux/of.h>
14+
#include <linux/platform_device.h>
15+
16+
/*
17+
* The APB0 clk has a configurable divisor.
18+
*
19+
* We must use a clk_div_table and not a regular power of 2
20+
* divisor here, because the first 2 values divide the clock
21+
* by 2.
22+
*/
23+
static const struct clk_div_table sun6i_a31_apb0_divs[] = {
24+
{ .val = 0, .div = 2, },
25+
{ .val = 1, .div = 2, },
26+
{ .val = 2, .div = 4, },
27+
{ .val = 3, .div = 8, },
28+
{ /* sentinel */ },
29+
};
30+
31+
static int sun6i_a31_apb0_clk_probe(struct platform_device *pdev)
32+
{
33+
struct device_node *np = pdev->dev.of_node;
34+
const char *clk_name = np->name;
35+
const char *clk_parent;
36+
struct resource *r;
37+
void __iomem *reg;
38+
struct clk *clk;
39+
40+
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
41+
reg = devm_ioremap_resource(&pdev->dev, r);
42+
if (IS_ERR(reg))
43+
return PTR_ERR(reg);
44+
45+
clk_parent = of_clk_get_parent_name(np, 0);
46+
if (!clk_parent)
47+
return -EINVAL;
48+
49+
of_property_read_string(np, "clock-output-names", &clk_name);
50+
51+
clk = clk_register_divider_table(&pdev->dev, clk_name, clk_parent,
52+
0, reg, 0, 2, 0, sun6i_a31_apb0_divs,
53+
NULL);
54+
if (IS_ERR(clk))
55+
return PTR_ERR(clk);
56+
57+
return of_clk_add_provider(np, of_clk_src_simple_get, clk);
58+
}
59+
60+
const struct of_device_id sun6i_a31_apb0_clk_dt_ids[] = {
61+
{ .compatible = "allwinner,sun6i-a31-apb0-clk" },
62+
{ /* sentinel */ }
63+
};
64+
65+
static struct platform_driver sun6i_a31_apb0_clk_driver = {
66+
.driver = {
67+
.name = "sun6i-a31-apb0-clk",
68+
.owner = THIS_MODULE,
69+
.of_match_table = sun6i_a31_apb0_clk_dt_ids,
70+
},
71+
.probe = sun6i_a31_apb0_clk_probe,
72+
};
73+
module_platform_driver(sun6i_a31_apb0_clk_driver);
74+
75+
MODULE_AUTHOR("Boris BREZILLON <[email protected]>");
76+
MODULE_DESCRIPTION("Allwinner A31 APB0 clock Driver");
77+
MODULE_LICENSE("GPL v2");

0 commit comments

Comments
 (0)