1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Copyright 2015 Chen-Yu Tsai |
4 | * |
5 | * Chen-Yu Tsai <wens@csie.org> |
6 | */ |
7 | |
8 | #include <linux/clk-provider.h> |
9 | #include <linux/io.h> |
10 | #include <linux/of.h> |
11 | #include <linux/of_address.h> |
12 | #include <linux/reset-controller.h> |
13 | #include <linux/slab.h> |
14 | #include <linux/spinlock.h> |
15 | |
16 | static DEFINE_SPINLOCK(ve_lock); |
17 | |
18 | #define SUN4I_VE_ENABLE 31 |
19 | #define SUN4I_VE_DIVIDER_SHIFT 16 |
20 | #define SUN4I_VE_DIVIDER_WIDTH 3 |
21 | #define SUN4I_VE_RESET 0 |
22 | |
23 | /* |
24 | * sunxi_ve_reset... - reset bit in ve clk registers handling |
25 | */ |
26 | |
27 | struct ve_reset_data { |
28 | void __iomem *reg; |
29 | spinlock_t *lock; |
30 | struct reset_controller_dev rcdev; |
31 | }; |
32 | |
33 | static int sunxi_ve_reset_assert(struct reset_controller_dev *rcdev, |
34 | unsigned long id) |
35 | { |
36 | struct ve_reset_data *data = container_of(rcdev, |
37 | struct ve_reset_data, |
38 | rcdev); |
39 | unsigned long flags; |
40 | u32 reg; |
41 | |
42 | spin_lock_irqsave(data->lock, flags); |
43 | |
44 | reg = readl(addr: data->reg); |
45 | writel(val: reg & ~BIT(SUN4I_VE_RESET), addr: data->reg); |
46 | |
47 | spin_unlock_irqrestore(lock: data->lock, flags); |
48 | |
49 | return 0; |
50 | } |
51 | |
52 | static int sunxi_ve_reset_deassert(struct reset_controller_dev *rcdev, |
53 | unsigned long id) |
54 | { |
55 | struct ve_reset_data *data = container_of(rcdev, |
56 | struct ve_reset_data, |
57 | rcdev); |
58 | unsigned long flags; |
59 | u32 reg; |
60 | |
61 | spin_lock_irqsave(data->lock, flags); |
62 | |
63 | reg = readl(addr: data->reg); |
64 | writel(val: reg | BIT(SUN4I_VE_RESET), addr: data->reg); |
65 | |
66 | spin_unlock_irqrestore(lock: data->lock, flags); |
67 | |
68 | return 0; |
69 | } |
70 | |
71 | static int sunxi_ve_of_xlate(struct reset_controller_dev *rcdev, |
72 | const struct of_phandle_args *reset_spec) |
73 | { |
74 | if (WARN_ON(reset_spec->args_count != 0)) |
75 | return -EINVAL; |
76 | |
77 | return 0; |
78 | } |
79 | |
80 | static const struct reset_control_ops sunxi_ve_reset_ops = { |
81 | .assert = sunxi_ve_reset_assert, |
82 | .deassert = sunxi_ve_reset_deassert, |
83 | }; |
84 | |
85 | static void __init sun4i_ve_clk_setup(struct device_node *node) |
86 | { |
87 | struct clk *clk; |
88 | struct clk_divider *div; |
89 | struct clk_gate *gate; |
90 | struct ve_reset_data *reset_data; |
91 | const char *parent; |
92 | const char *clk_name = node->name; |
93 | void __iomem *reg; |
94 | int err; |
95 | |
96 | reg = of_io_request_and_map(device: node, index: 0, name: of_node_full_name(np: node)); |
97 | if (IS_ERR(ptr: reg)) |
98 | return; |
99 | |
100 | div = kzalloc(size: sizeof(*div), GFP_KERNEL); |
101 | if (!div) |
102 | goto err_unmap; |
103 | |
104 | gate = kzalloc(size: sizeof(*gate), GFP_KERNEL); |
105 | if (!gate) |
106 | goto err_free_div; |
107 | |
108 | of_property_read_string(np: node, propname: "clock-output-names" , out_string: &clk_name); |
109 | parent = of_clk_get_parent_name(np: node, index: 0); |
110 | |
111 | gate->reg = reg; |
112 | gate->bit_idx = SUN4I_VE_ENABLE; |
113 | gate->lock = &ve_lock; |
114 | |
115 | div->reg = reg; |
116 | div->shift = SUN4I_VE_DIVIDER_SHIFT; |
117 | div->width = SUN4I_VE_DIVIDER_WIDTH; |
118 | div->lock = &ve_lock; |
119 | |
120 | clk = clk_register_composite(NULL, name: clk_name, parent_names: &parent, num_parents: 1, |
121 | NULL, NULL, |
122 | rate_hw: &div->hw, rate_ops: &clk_divider_ops, |
123 | gate_hw: &gate->hw, gate_ops: &clk_gate_ops, |
124 | CLK_SET_RATE_PARENT); |
125 | if (IS_ERR(ptr: clk)) |
126 | goto err_free_gate; |
127 | |
128 | err = of_clk_add_provider(np: node, clk_src_get: of_clk_src_simple_get, data: clk); |
129 | if (err) |
130 | goto err_unregister_clk; |
131 | |
132 | reset_data = kzalloc(size: sizeof(*reset_data), GFP_KERNEL); |
133 | if (!reset_data) |
134 | goto err_del_provider; |
135 | |
136 | reset_data->reg = reg; |
137 | reset_data->lock = &ve_lock; |
138 | reset_data->rcdev.nr_resets = 1; |
139 | reset_data->rcdev.ops = &sunxi_ve_reset_ops; |
140 | reset_data->rcdev.of_node = node; |
141 | reset_data->rcdev.of_xlate = sunxi_ve_of_xlate; |
142 | reset_data->rcdev.of_reset_n_cells = 0; |
143 | err = reset_controller_register(rcdev: &reset_data->rcdev); |
144 | if (err) |
145 | goto err_free_reset; |
146 | |
147 | return; |
148 | |
149 | err_free_reset: |
150 | kfree(objp: reset_data); |
151 | err_del_provider: |
152 | of_clk_del_provider(np: node); |
153 | err_unregister_clk: |
154 | clk_unregister(clk); |
155 | err_free_gate: |
156 | kfree(objp: gate); |
157 | err_free_div: |
158 | kfree(objp: div); |
159 | err_unmap: |
160 | iounmap(addr: reg); |
161 | } |
162 | CLK_OF_DECLARE(sun4i_ve, "allwinner,sun4i-a10-ve-clk" , |
163 | sun4i_ve_clk_setup); |
164 | |