Commit | Line | Data |
---|---|---|
9c8176bf CYT |
1 | /* |
2 | * Copyright 2014 Chen-Yu Tsai | |
3 | * | |
4 | * Chen-Yu Tsai <wens@csie.org> | |
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 | ||
9dfefe8c | 17 | #include <linux/clk.h> |
9c8176bf | 18 | #include <linux/clk-provider.h> |
8f2bf2ad CYT |
19 | #include <linux/slab.h> |
20 | #include <linux/spinlock.h> | |
9c8176bf CYT |
21 | #include <linux/of_address.h> |
22 | ||
8f2bf2ad CYT |
23 | #define SUN8I_MBUS_ENABLE 31 |
24 | #define SUN8I_MBUS_MUX_SHIFT 24 | |
25 | #define SUN8I_MBUS_MUX_MASK 0x3 | |
26 | #define SUN8I_MBUS_DIV_SHIFT 0 | |
27 | #define SUN8I_MBUS_DIV_WIDTH 3 | |
28 | #define SUN8I_MBUS_MAX_PARENTS 4 | |
9c8176bf | 29 | |
8f2bf2ad | 30 | static DEFINE_SPINLOCK(sun8i_a23_mbus_lock); |
9c8176bf | 31 | |
8f2bf2ad | 32 | static void __init sun8i_a23_mbus_setup(struct device_node *node) |
9c8176bf | 33 | { |
8f2bf2ad | 34 | int num_parents = of_clk_get_parent_count(node); |
1295e36a | 35 | const char **parents; |
8f2bf2ad CYT |
36 | const char *clk_name = node->name; |
37 | struct resource res; | |
38 | struct clk_divider *div; | |
39 | struct clk_gate *gate; | |
40 | struct clk_mux *mux; | |
41 | struct clk *clk; | |
42 | void __iomem *reg; | |
43 | int err; | |
9c8176bf | 44 | |
1295e36a SB |
45 | parents = kcalloc(num_parents, sizeof(*parents), GFP_KERNEL); |
46 | if (!parents) | |
47 | return; | |
48 | ||
8f2bf2ad | 49 | reg = of_io_request_and_map(node, 0, of_node_full_name(node)); |
fbe359f1 | 50 | if (IS_ERR(reg)) { |
8f2bf2ad | 51 | pr_err("Could not get registers for sun8i-mbus-clk\n"); |
1295e36a | 52 | goto err_free_parents; |
8f2bf2ad | 53 | } |
9c8176bf | 54 | |
8f2bf2ad CYT |
55 | div = kzalloc(sizeof(*div), GFP_KERNEL); |
56 | if (!div) | |
57 | goto err_unmap; | |
9c8176bf | 58 | |
8f2bf2ad CYT |
59 | mux = kzalloc(sizeof(*mux), GFP_KERNEL); |
60 | if (!mux) | |
61 | goto err_free_div; | |
9c8176bf | 62 | |
8f2bf2ad CYT |
63 | gate = kzalloc(sizeof(*gate), GFP_KERNEL); |
64 | if (!gate) | |
65 | goto err_free_mux; | |
9c8176bf | 66 | |
8f2bf2ad CYT |
67 | of_property_read_string(node, "clock-output-names", &clk_name); |
68 | of_clk_parent_fill(node, parents, num_parents); | |
9c8176bf | 69 | |
8f2bf2ad CYT |
70 | gate->reg = reg; |
71 | gate->bit_idx = SUN8I_MBUS_ENABLE; | |
72 | gate->lock = &sun8i_a23_mbus_lock; | |
9c8176bf | 73 | |
8f2bf2ad CYT |
74 | div->reg = reg; |
75 | div->shift = SUN8I_MBUS_DIV_SHIFT; | |
76 | div->width = SUN8I_MBUS_DIV_WIDTH; | |
77 | div->lock = &sun8i_a23_mbus_lock; | |
9c8176bf | 78 | |
8f2bf2ad CYT |
79 | mux->reg = reg; |
80 | mux->shift = SUN8I_MBUS_MUX_SHIFT; | |
81 | mux->mask = SUN8I_MBUS_MUX_MASK; | |
82 | mux->lock = &sun8i_a23_mbus_lock; | |
7c74c220 | 83 | |
9919d44f | 84 | /* The MBUS clocks needs to be always enabled */ |
8f2bf2ad CYT |
85 | clk = clk_register_composite(NULL, clk_name, parents, num_parents, |
86 | &mux->hw, &clk_mux_ops, | |
87 | &div->hw, &clk_divider_ops, | |
88 | &gate->hw, &clk_gate_ops, | |
9919d44f | 89 | CLK_IS_CRITICAL); |
8f2bf2ad CYT |
90 | if (IS_ERR(clk)) |
91 | goto err_free_gate; | |
7c74c220 | 92 | |
8f2bf2ad CYT |
93 | err = of_clk_add_provider(node, of_clk_src_simple_get, clk); |
94 | if (err) | |
95 | goto err_unregister_clk; | |
9c8176bf | 96 | |
1295e36a | 97 | kfree(parents); /* parents is deep copied */ |
8f2bf2ad CYT |
98 | |
99 | return; | |
100 | ||
101 | err_unregister_clk: | |
102 | /* TODO: The composite clock stuff will leak a bit here. */ | |
103 | clk_unregister(clk); | |
104 | err_free_gate: | |
105 | kfree(gate); | |
106 | err_free_mux: | |
107 | kfree(mux); | |
108 | err_free_div: | |
109 | kfree(div); | |
110 | err_unmap: | |
111 | iounmap(reg); | |
112 | of_address_to_resource(node, 0, &res); | |
113 | release_mem_region(res.start, resource_size(&res)); | |
1295e36a SB |
114 | err_free_parents: |
115 | kfree(parents); | |
9c8176bf CYT |
116 | } |
117 | CLK_OF_DECLARE(sun8i_a23_mbus, "allwinner,sun8i-a23-mbus-clk", sun8i_a23_mbus_setup); |