/src/u-boot/drivers/clk/imx/clk-gate2.c
Line | Count | Source |
1 | | // SPDX-License-Identifier: GPL-2.0+ |
2 | | /* |
3 | | * Copyright (C) 2019 DENX Software Engineering |
4 | | * Lukasz Majewski, DENX Software Engineering, lukma@denx.de |
5 | | * |
6 | | * Copyright (C) 2010-2011 Canonical Ltd <jeremy.kerr@canonical.com> |
7 | | * Copyright (C) 2011-2012 Mike Turquette, Linaro Ltd <mturquette@linaro.org> |
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 version 2 as |
11 | | * published by the Free Software Foundation. |
12 | | * |
13 | | * Gated clock implementation |
14 | | * |
15 | | */ |
16 | | |
17 | | #include <asm/io.h> |
18 | | #include <malloc.h> |
19 | | #include <clk-uclass.h> |
20 | | #include <dm/device.h> |
21 | | #include <dm/devres.h> |
22 | | #include <linux/bug.h> |
23 | | #include <linux/clk-provider.h> |
24 | | #include <clk.h> |
25 | | #include "clk.h" |
26 | | #include <linux/err.h> |
27 | | |
28 | 0 | #define UBOOT_DM_CLK_IMX_GATE2 "imx_clk_gate2" |
29 | | |
30 | | struct clk_gate2 { |
31 | | struct clk clk; |
32 | | void __iomem *reg; |
33 | | u8 bit_idx; |
34 | | u8 cgr_val; |
35 | | u8 flags; |
36 | | unsigned int *share_count; |
37 | | }; |
38 | | |
39 | 0 | #define to_clk_gate2(_clk) container_of(_clk, struct clk_gate2, clk) |
40 | | |
41 | | static int clk_gate2_enable(struct clk *clk) |
42 | 0 | { |
43 | 0 | struct clk_gate2 *gate = to_clk_gate2(clk); |
44 | 0 | u32 reg; |
45 | |
|
46 | 0 | if (gate->share_count && (*gate->share_count)++ > 0) |
47 | 0 | return 0; |
48 | | |
49 | 0 | reg = readl(gate->reg); |
50 | 0 | reg &= ~(3 << gate->bit_idx); |
51 | 0 | reg |= gate->cgr_val << gate->bit_idx; |
52 | 0 | writel(reg, gate->reg); |
53 | |
|
54 | 0 | return 0; |
55 | 0 | } |
56 | | |
57 | | static int clk_gate2_disable(struct clk *clk) |
58 | 0 | { |
59 | 0 | struct clk_gate2 *gate = to_clk_gate2(clk); |
60 | 0 | u32 reg; |
61 | |
|
62 | 0 | if (gate->share_count) { |
63 | 0 | if (WARN_ON(*gate->share_count == 0)) |
64 | 0 | return 0; |
65 | 0 | else if (--(*gate->share_count) > 0) |
66 | 0 | return 0; |
67 | 0 | } |
68 | | |
69 | 0 | reg = readl(gate->reg); |
70 | 0 | reg &= ~(3 << gate->bit_idx); |
71 | 0 | writel(reg, gate->reg); |
72 | |
|
73 | 0 | return 0; |
74 | 0 | } |
75 | | |
76 | | static ulong clk_gate2_set_rate(struct clk *clk, ulong rate) |
77 | 0 | { |
78 | 0 | struct clk *parent = clk_get_parent(clk); |
79 | |
|
80 | 0 | if (parent) |
81 | 0 | return clk_set_rate(parent, rate); |
82 | | |
83 | 0 | return -ENODEV; |
84 | 0 | } |
85 | | |
86 | | static const struct clk_ops clk_gate2_ops = { |
87 | | .set_rate = clk_gate2_set_rate, |
88 | | .enable = clk_gate2_enable, |
89 | | .disable = clk_gate2_disable, |
90 | | .get_rate = clk_generic_get_rate, |
91 | | }; |
92 | | |
93 | | struct clk *clk_register_gate2(struct udevice *dev, const char *name, |
94 | | const char *parent_name, unsigned long flags, |
95 | | void __iomem *reg, u8 bit_idx, u8 cgr_val, |
96 | | u8 clk_gate2_flags, unsigned int *share_count) |
97 | 0 | { |
98 | 0 | struct clk_gate2 *gate; |
99 | 0 | struct clk *clk; |
100 | 0 | int ret; |
101 | |
|
102 | 0 | gate = kzalloc(sizeof(*gate), GFP_KERNEL); |
103 | 0 | if (!gate) |
104 | 0 | return ERR_PTR(-ENOMEM); |
105 | | |
106 | 0 | gate->reg = reg; |
107 | 0 | gate->bit_idx = bit_idx; |
108 | 0 | gate->cgr_val = cgr_val; |
109 | 0 | gate->flags = clk_gate2_flags; |
110 | 0 | gate->share_count = share_count; |
111 | |
|
112 | 0 | clk = &gate->clk; |
113 | |
|
114 | 0 | ret = clk_register(clk, UBOOT_DM_CLK_IMX_GATE2, name, |
115 | 0 | clk_resolve_parent_clk(dev, parent_name)); |
116 | 0 | if (ret) { |
117 | 0 | kfree(gate); |
118 | 0 | return ERR_PTR(ret); |
119 | 0 | } |
120 | | |
121 | 0 | return clk; |
122 | 0 | } |
123 | | |
124 | | U_BOOT_DRIVER(clk_gate2) = { |
125 | | .name = UBOOT_DM_CLK_IMX_GATE2, |
126 | | .id = UCLASS_CLK, |
127 | | .ops = &clk_gate2_ops, |
128 | | .flags = DM_FLAG_PRE_RELOC, |
129 | | }; |