1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Copyright 2013-2015 Emilio López
5 * Emilio López <emilio@elopez.com.ar>
9 #include <linux/clk-provider.h>
12 #include <linux/of_address.h>
13 #include <linux/reset-controller.h>
14 #include <linux/slab.h>
15 #include <linux/spinlock.h>
19 * sunxi_usb_reset... - reset bits in usb clk registers handling
22 struct usb_reset_data
{
26 struct reset_controller_dev rcdev
;
29 static int sunxi_usb_reset_assert(struct reset_controller_dev
*rcdev
,
32 struct usb_reset_data
*data
= container_of(rcdev
,
33 struct usb_reset_data
,
38 clk_prepare_enable(data
->clk
);
39 spin_lock_irqsave(data
->lock
, flags
);
41 reg
= readl(data
->reg
);
42 writel(reg
& ~BIT(id
), data
->reg
);
44 spin_unlock_irqrestore(data
->lock
, flags
);
45 clk_disable_unprepare(data
->clk
);
50 static int sunxi_usb_reset_deassert(struct reset_controller_dev
*rcdev
,
53 struct usb_reset_data
*data
= container_of(rcdev
,
54 struct usb_reset_data
,
59 clk_prepare_enable(data
->clk
);
60 spin_lock_irqsave(data
->lock
, flags
);
62 reg
= readl(data
->reg
);
63 writel(reg
| BIT(id
), data
->reg
);
65 spin_unlock_irqrestore(data
->lock
, flags
);
66 clk_disable_unprepare(data
->clk
);
71 static const struct reset_control_ops sunxi_usb_reset_ops
= {
72 .assert = sunxi_usb_reset_assert
,
73 .deassert
= sunxi_usb_reset_deassert
,
77 * sunxi_usb_clk_setup() - Setup function for usb gate clocks
80 #define SUNXI_USB_MAX_SIZE 32
88 static void __init
sunxi_usb_clk_setup(struct device_node
*node
,
89 const struct usb_clk_data
*data
,
92 struct clk_onecell_data
*clk_data
;
93 struct usb_reset_data
*reset_data
;
94 const char *clk_parent
;
101 reg
= of_io_request_and_map(node
, 0, of_node_full_name(node
));
105 clk_parent
= of_clk_get_parent_name(node
, 0);
109 /* Worst-case size approximation and memory allocation */
110 qty
= find_last_bit((unsigned long *)&data
->clk_mask
,
113 clk_data
= kmalloc(sizeof(struct clk_onecell_data
), GFP_KERNEL
);
117 clk_data
->clks
= kcalloc(qty
+ 1, sizeof(struct clk
*), GFP_KERNEL
);
118 if (!clk_data
->clks
) {
123 for_each_set_bit(i
, (unsigned long *)&data
->clk_mask
,
124 SUNXI_USB_MAX_SIZE
) {
125 of_property_read_string_index(node
, "clock-output-names",
127 clk_data
->clks
[i
] = clk_register_gate(NULL
, clk_name
,
130 WARN_ON(IS_ERR(clk_data
->clks
[i
]));
135 /* Adjust to the real max */
136 clk_data
->clk_num
= i
;
138 of_clk_add_provider(node
, of_clk_src_onecell_get
, clk_data
);
140 /* Register a reset controller for usb with reset bits */
141 if (data
->reset_mask
== 0)
144 reset_data
= kzalloc(sizeof(*reset_data
), GFP_KERNEL
);
148 if (data
->reset_needs_clk
) {
149 reset_data
->clk
= of_clk_get(node
, 0);
150 if (IS_ERR(reset_data
->clk
)) {
151 pr_err("Could not get clock for reset controls\n");
157 reset_data
->reg
= reg
;
158 reset_data
->lock
= lock
;
159 reset_data
->rcdev
.nr_resets
= __fls(data
->reset_mask
) + 1;
160 reset_data
->rcdev
.ops
= &sunxi_usb_reset_ops
;
161 reset_data
->rcdev
.of_node
= node
;
162 reset_controller_register(&reset_data
->rcdev
);
165 static const struct usb_clk_data sun4i_a10_usb_clk_data __initconst
= {
166 .clk_mask
= BIT(8) | BIT(7) | BIT(6),
167 .reset_mask
= BIT(2) | BIT(1) | BIT(0),
170 static DEFINE_SPINLOCK(sun4i_a10_usb_lock
);
172 static void __init
sun4i_a10_usb_setup(struct device_node
*node
)
174 sunxi_usb_clk_setup(node
, &sun4i_a10_usb_clk_data
, &sun4i_a10_usb_lock
);
176 CLK_OF_DECLARE(sun4i_a10_usb
, "allwinner,sun4i-a10-usb-clk", sun4i_a10_usb_setup
);
178 static const struct usb_clk_data sun5i_a13_usb_clk_data __initconst
= {
179 .clk_mask
= BIT(8) | BIT(6),
180 .reset_mask
= BIT(1) | BIT(0),
183 static void __init
sun5i_a13_usb_setup(struct device_node
*node
)
185 sunxi_usb_clk_setup(node
, &sun5i_a13_usb_clk_data
, &sun4i_a10_usb_lock
);
187 CLK_OF_DECLARE(sun5i_a13_usb
, "allwinner,sun5i-a13-usb-clk", sun5i_a13_usb_setup
);
189 static const struct usb_clk_data sun6i_a31_usb_clk_data __initconst
= {
190 .clk_mask
= BIT(18) | BIT(17) | BIT(16) | BIT(10) | BIT(9) | BIT(8),
191 .reset_mask
= BIT(2) | BIT(1) | BIT(0),
194 static void __init
sun6i_a31_usb_setup(struct device_node
*node
)
196 sunxi_usb_clk_setup(node
, &sun6i_a31_usb_clk_data
, &sun4i_a10_usb_lock
);
198 CLK_OF_DECLARE(sun6i_a31_usb
, "allwinner,sun6i-a31-usb-clk", sun6i_a31_usb_setup
);
200 static const struct usb_clk_data sun8i_a23_usb_clk_data __initconst
= {
201 .clk_mask
= BIT(16) | BIT(11) | BIT(10) | BIT(9) | BIT(8),
202 .reset_mask
= BIT(2) | BIT(1) | BIT(0),
205 static void __init
sun8i_a23_usb_setup(struct device_node
*node
)
207 sunxi_usb_clk_setup(node
, &sun8i_a23_usb_clk_data
, &sun4i_a10_usb_lock
);
209 CLK_OF_DECLARE(sun8i_a23_usb
, "allwinner,sun8i-a23-usb-clk", sun8i_a23_usb_setup
);
211 static const struct usb_clk_data sun8i_h3_usb_clk_data __initconst
= {
212 .clk_mask
= BIT(19) | BIT(18) | BIT(17) | BIT(16) |
213 BIT(11) | BIT(10) | BIT(9) | BIT(8),
214 .reset_mask
= BIT(3) | BIT(2) | BIT(1) | BIT(0),
217 static void __init
sun8i_h3_usb_setup(struct device_node
*node
)
219 sunxi_usb_clk_setup(node
, &sun8i_h3_usb_clk_data
, &sun4i_a10_usb_lock
);
221 CLK_OF_DECLARE(sun8i_h3_usb
, "allwinner,sun8i-h3-usb-clk", sun8i_h3_usb_setup
);
223 static const struct usb_clk_data sun9i_a80_usb_mod_data __initconst
= {
224 .clk_mask
= BIT(6) | BIT(5) | BIT(4) | BIT(3) | BIT(2) | BIT(1),
225 .reset_mask
= BIT(19) | BIT(18) | BIT(17),
226 .reset_needs_clk
= 1,
229 static DEFINE_SPINLOCK(a80_usb_mod_lock
);
231 static void __init
sun9i_a80_usb_mod_setup(struct device_node
*node
)
233 sunxi_usb_clk_setup(node
, &sun9i_a80_usb_mod_data
, &a80_usb_mod_lock
);
235 CLK_OF_DECLARE(sun9i_a80_usb_mod
, "allwinner,sun9i-a80-usb-mod-clk", sun9i_a80_usb_mod_setup
);
237 static const struct usb_clk_data sun9i_a80_usb_phy_data __initconst
= {
238 .clk_mask
= BIT(10) | BIT(5) | BIT(4) | BIT(3) | BIT(2) | BIT(1),
239 .reset_mask
= BIT(21) | BIT(20) | BIT(19) | BIT(18) | BIT(17),
240 .reset_needs_clk
= 1,
243 static DEFINE_SPINLOCK(a80_usb_phy_lock
);
245 static void __init
sun9i_a80_usb_phy_setup(struct device_node
*node
)
247 sunxi_usb_clk_setup(node
, &sun9i_a80_usb_phy_data
, &a80_usb_phy_lock
);
249 CLK_OF_DECLARE(sun9i_a80_usb_phy
, "allwinner,sun9i-a80-usb-phy-clk", sun9i_a80_usb_phy_setup
);