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