1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Copyright (C) 2015 Jens Kuske <jenskuske@gmail.com> |
4 | * |
5 | * Based on clk-simple-gates.c, which is: |
6 | * Copyright 2015 Maxime Ripard |
7 | * |
8 | * Maxime Ripard <maxime.ripard@free-electrons.com> |
9 | */ |
10 | |
11 | #include <linux/clk-provider.h> |
12 | #include <linux/io.h> |
13 | #include <linux/of.h> |
14 | #include <linux/of_address.h> |
15 | #include <linux/slab.h> |
16 | #include <linux/spinlock.h> |
17 | |
18 | static DEFINE_SPINLOCK(gates_lock); |
19 | |
20 | static void __init sun8i_h3_bus_gates_init(struct device_node *node) |
21 | { |
22 | static const char * const names[] = { "ahb1" , "ahb2" , "apb1" , "apb2" }; |
23 | enum { AHB1, AHB2, APB1, APB2, PARENT_MAX } clk_parent; |
24 | const char *parents[PARENT_MAX]; |
25 | struct clk_onecell_data *clk_data; |
26 | const char *clk_name; |
27 | struct property *prop; |
28 | struct resource res; |
29 | void __iomem *clk_reg; |
30 | void __iomem *reg; |
31 | const __be32 *p; |
32 | int number, i; |
33 | u8 clk_bit; |
34 | int index; |
35 | |
36 | reg = of_io_request_and_map(device: node, index: 0, name: of_node_full_name(np: node)); |
37 | if (IS_ERR(ptr: reg)) |
38 | return; |
39 | |
40 | for (i = 0; i < ARRAY_SIZE(names); i++) { |
41 | int idx = of_property_match_string(np: node, propname: "clock-names" , |
42 | string: names[i]); |
43 | if (idx < 0) |
44 | return; |
45 | |
46 | parents[i] = of_clk_get_parent_name(np: node, index: idx); |
47 | } |
48 | |
49 | clk_data = kmalloc(size: sizeof(struct clk_onecell_data), GFP_KERNEL); |
50 | if (!clk_data) |
51 | goto err_unmap; |
52 | |
53 | number = of_property_count_u32_elems(np: node, propname: "clock-indices" ); |
54 | of_property_read_u32_index(np: node, propname: "clock-indices" , index: number - 1, out_value: &number); |
55 | |
56 | clk_data->clks = kcalloc(n: number + 1, size: sizeof(struct clk *), GFP_KERNEL); |
57 | if (!clk_data->clks) |
58 | goto err_free_data; |
59 | |
60 | i = 0; |
61 | of_property_for_each_u32(node, "clock-indices" , prop, p, index) { |
62 | of_property_read_string_index(np: node, propname: "clock-output-names" , |
63 | index: i, output: &clk_name); |
64 | |
65 | if (index == 17 || (index >= 29 && index <= 31)) |
66 | clk_parent = AHB2; |
67 | else if (index <= 63 || index >= 128) |
68 | clk_parent = AHB1; |
69 | else if (index >= 64 && index <= 95) |
70 | clk_parent = APB1; |
71 | else if (index >= 96 && index <= 127) |
72 | clk_parent = APB2; |
73 | else { |
74 | WARN_ON(true); |
75 | continue; |
76 | } |
77 | |
78 | clk_reg = reg + 4 * (index / 32); |
79 | clk_bit = index % 32; |
80 | |
81 | clk_data->clks[index] = clk_register_gate(NULL, name: clk_name, |
82 | parent_name: parents[clk_parent], |
83 | flags: 0, reg: clk_reg, bit_idx: clk_bit, |
84 | clk_gate_flags: 0, lock: &gates_lock); |
85 | i++; |
86 | |
87 | if (IS_ERR(ptr: clk_data->clks[index])) { |
88 | WARN_ON(true); |
89 | continue; |
90 | } |
91 | } |
92 | |
93 | clk_data->clk_num = number + 1; |
94 | of_clk_add_provider(np: node, clk_src_get: of_clk_src_onecell_get, data: clk_data); |
95 | |
96 | return; |
97 | |
98 | err_free_data: |
99 | kfree(objp: clk_data); |
100 | err_unmap: |
101 | iounmap(addr: reg); |
102 | of_address_to_resource(dev: node, index: 0, r: &res); |
103 | release_mem_region(res.start, resource_size(&res)); |
104 | } |
105 | |
106 | CLK_OF_DECLARE(sun8i_h3_bus_gates, "allwinner,sun8i-h3-bus-gates-clk" , |
107 | sun8i_h3_bus_gates_init); |
108 | CLK_OF_DECLARE(sun8i_a83t_bus_gates, "allwinner,sun8i-a83t-bus-gates-clk" , |
109 | sun8i_h3_bus_gates_init); |
110 | |