Commit | Line | Data |
---|---|---|
fa4d0ca1 MR |
1 | /* |
2 | * Copyright 2015 Maxime Ripard | |
3 | * | |
4 | * Maxime Ripard <maxime.ripard@free-electrons.com> | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License as published by | |
8 | * the Free Software Foundation; either version 2 of the License, or | |
9 | * (at your option) any later version. | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | * GNU General Public License for more details. | |
15 | */ | |
16 | ||
17 | #include <linux/clk-provider.h> | |
18 | #include <linux/of.h> | |
19 | #include <linux/of_address.h> | |
20 | #include <linux/slab.h> | |
21 | #include <linux/spinlock.h> | |
22 | ||
23 | #define SUN4I_A10_PLL3_GATE_BIT 31 | |
24 | #define SUN4I_A10_PLL3_DIV_WIDTH 7 | |
25 | #define SUN4I_A10_PLL3_DIV_SHIFT 0 | |
26 | ||
27 | static DEFINE_SPINLOCK(sun4i_a10_pll3_lock); | |
28 | ||
29 | static void __init sun4i_a10_pll3_setup(struct device_node *node) | |
30 | { | |
31 | const char *clk_name = node->name, *parent; | |
32 | struct clk_multiplier *mult; | |
33 | struct clk_gate *gate; | |
34 | struct resource res; | |
35 | void __iomem *reg; | |
36 | struct clk *clk; | |
37 | int ret; | |
38 | ||
39 | of_property_read_string(node, "clock-output-names", &clk_name); | |
40 | parent = of_clk_get_parent_name(node, 0); | |
41 | ||
42 | reg = of_io_request_and_map(node, 0, of_node_full_name(node)); | |
43 | if (IS_ERR(reg)) { | |
44 | pr_err("%s: Could not map the clock registers\n", clk_name); | |
45 | return; | |
46 | } | |
47 | ||
48 | gate = kzalloc(sizeof(*gate), GFP_KERNEL); | |
49 | if (!gate) | |
50 | goto err_unmap; | |
51 | ||
52 | gate->reg = reg; | |
53 | gate->bit_idx = SUN4I_A10_PLL3_GATE_BIT; | |
54 | gate->lock = &sun4i_a10_pll3_lock; | |
55 | ||
56 | mult = kzalloc(sizeof(*mult), GFP_KERNEL); | |
57 | if (!mult) | |
58 | goto err_free_gate; | |
59 | ||
60 | mult->reg = reg; | |
61 | mult->shift = SUN4I_A10_PLL3_DIV_SHIFT; | |
62 | mult->width = SUN4I_A10_PLL3_DIV_WIDTH; | |
63 | mult->lock = &sun4i_a10_pll3_lock; | |
64 | ||
65 | clk = clk_register_composite(NULL, clk_name, | |
66 | &parent, 1, | |
67 | NULL, NULL, | |
68 | &mult->hw, &clk_multiplier_ops, | |
69 | &gate->hw, &clk_gate_ops, | |
70 | 0); | |
71 | if (IS_ERR(clk)) { | |
72 | pr_err("%s: Couldn't register the clock\n", clk_name); | |
73 | goto err_free_mult; | |
74 | } | |
75 | ||
76 | ret = of_clk_add_provider(node, of_clk_src_simple_get, clk); | |
77 | if (ret) { | |
78 | pr_err("%s: Couldn't register DT provider\n", | |
79 | clk_name); | |
80 | goto err_clk_unregister; | |
81 | } | |
82 | ||
83 | return; | |
84 | ||
85 | err_clk_unregister: | |
86 | clk_unregister_composite(clk); | |
87 | err_free_mult: | |
88 | kfree(mult); | |
89 | err_free_gate: | |
90 | kfree(gate); | |
91 | err_unmap: | |
92 | iounmap(reg); | |
93 | of_address_to_resource(node, 0, &res); | |
94 | release_mem_region(res.start, resource_size(&res)); | |
95 | } | |
96 | ||
97 | CLK_OF_DECLARE(sun4i_a10_pll3, "allwinner,sun4i-a10-pll3-clk", | |
98 | sun4i_a10_pll3_setup); |