Commit | Line | Data |
---|---|---|
ab6e23a4 JK |
1 | /* |
2 | * Copyright (C) 2015 Jens Kuske <jenskuske@gmail.com> | |
3 | * | |
4 | * Based on clk-simple-gates.c, which is: | |
5 | * Copyright 2015 Maxime Ripard | |
6 | * | |
7 | * Maxime Ripard <maxime.ripard@free-electrons.com> | |
8 | * | |
9 | * This program is free software; you can redistribute it and/or modify | |
10 | * it under the terms of the GNU General Public License as published by | |
11 | * the Free Software Foundation; either version 2 of the License, or | |
12 | * (at your option) any later version. | |
13 | * | |
14 | * This program is distributed in the hope that it will be useful, | |
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
17 | * GNU General Public License for more details. | |
18 | */ | |
19 | ||
ab6e23a4 JK |
20 | #include <linux/clk-provider.h> |
21 | #include <linux/of.h> | |
22 | #include <linux/of_address.h> | |
23 | #include <linux/slab.h> | |
24 | #include <linux/spinlock.h> | |
25 | ||
26 | static DEFINE_SPINLOCK(gates_lock); | |
27 | ||
28 | static void __init sun8i_h3_bus_gates_init(struct device_node *node) | |
29 | { | |
30 | static const char * const names[] = { "ahb1", "ahb2", "apb1", "apb2" }; | |
31 | enum { AHB1, AHB2, APB1, APB2, PARENT_MAX } clk_parent; | |
32 | const char *parents[PARENT_MAX]; | |
33 | struct clk_onecell_data *clk_data; | |
34 | const char *clk_name; | |
35 | struct property *prop; | |
36 | struct resource res; | |
37 | void __iomem *clk_reg; | |
38 | void __iomem *reg; | |
39 | const __be32 *p; | |
40 | int number, i; | |
41 | u8 clk_bit; | |
b1558f16 | 42 | int index; |
ab6e23a4 JK |
43 | |
44 | reg = of_io_request_and_map(node, 0, of_node_full_name(node)); | |
45 | if (IS_ERR(reg)) | |
46 | return; | |
47 | ||
48 | for (i = 0; i < ARRAY_SIZE(names); i++) { | |
fee3103a AH |
49 | int idx = of_property_match_string(node, "clock-names", |
50 | names[i]); | |
51 | if (idx < 0) | |
ab6e23a4 JK |
52 | return; |
53 | ||
fee3103a | 54 | parents[i] = of_clk_get_parent_name(node, idx); |
ab6e23a4 JK |
55 | } |
56 | ||
57 | clk_data = kmalloc(sizeof(struct clk_onecell_data), GFP_KERNEL); | |
58 | if (!clk_data) | |
59 | goto err_unmap; | |
60 | ||
61 | number = of_property_count_u32_elems(node, "clock-indices"); | |
62 | of_property_read_u32_index(node, "clock-indices", number - 1, &number); | |
63 | ||
64 | clk_data->clks = kcalloc(number + 1, sizeof(struct clk *), GFP_KERNEL); | |
65 | if (!clk_data->clks) | |
66 | goto err_free_data; | |
67 | ||
68 | i = 0; | |
69 | of_property_for_each_u32(node, "clock-indices", prop, p, index) { | |
70 | of_property_read_string_index(node, "clock-output-names", | |
71 | i, &clk_name); | |
72 | ||
73 | if (index == 17 || (index >= 29 && index <= 31)) | |
74 | clk_parent = AHB2; | |
75 | else if (index <= 63 || index >= 128) | |
76 | clk_parent = AHB1; | |
77 | else if (index >= 64 && index <= 95) | |
78 | clk_parent = APB1; | |
79 | else if (index >= 96 && index <= 127) | |
80 | clk_parent = APB2; | |
81 | ||
82 | clk_reg = reg + 4 * (index / 32); | |
83 | clk_bit = index % 32; | |
84 | ||
85 | clk_data->clks[index] = clk_register_gate(NULL, clk_name, | |
86 | parents[clk_parent], | |
87 | 0, clk_reg, clk_bit, | |
88 | 0, &gates_lock); | |
89 | i++; | |
90 | ||
91 | if (IS_ERR(clk_data->clks[index])) { | |
92 | WARN_ON(true); | |
93 | continue; | |
94 | } | |
95 | } | |
96 | ||
97 | clk_data->clk_num = number + 1; | |
98 | of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); | |
99 | ||
100 | return; | |
101 | ||
102 | err_free_data: | |
103 | kfree(clk_data); | |
104 | err_unmap: | |
105 | iounmap(reg); | |
106 | of_address_to_resource(node, 0, &res); | |
107 | release_mem_region(res.start, resource_size(&res)); | |
108 | } | |
109 | ||
110 | CLK_OF_DECLARE(sun8i_h3_bus_gates, "allwinner,sun8i-h3-bus-gates-clk", | |
111 | sun8i_h3_bus_gates_init); | |
be338e4c VP |
112 | CLK_OF_DECLARE(sun8i_a83t_bus_gates, "allwinner,sun8i-a83t-bus-gates-clk", |
113 | sun8i_h3_bus_gates_init); |