.. | .. |
---|
1 | 1 | // SPDX-License-Identifier: GPL-2.0 |
---|
2 | | -/** |
---|
| 2 | +/* |
---|
3 | 3 | * dwc3-of-simple.c - OF glue layer for simple integrations |
---|
4 | 4 | * |
---|
5 | | - * Copyright (c) 2015 Texas Instruments Incorporated - http://www.ti.com |
---|
| 5 | + * Copyright (c) 2015 Texas Instruments Incorporated - https://www.ti.com |
---|
6 | 6 | * |
---|
7 | 7 | * Author: Felipe Balbi <balbi@ti.com> |
---|
8 | 8 | * |
---|
.. | .. |
---|
24 | 24 | |
---|
25 | 25 | struct dwc3_of_simple { |
---|
26 | 26 | struct device *dev; |
---|
27 | | - struct clk **clks; |
---|
| 27 | + struct clk_bulk_data *clks; |
---|
28 | 28 | int num_clocks; |
---|
29 | 29 | struct reset_control *resets; |
---|
30 | | - bool pulse_resets; |
---|
31 | 30 | bool need_reset; |
---|
32 | 31 | }; |
---|
33 | | - |
---|
34 | | -static int dwc3_of_simple_clk_init(struct dwc3_of_simple *simple, int count) |
---|
35 | | -{ |
---|
36 | | - struct device *dev = simple->dev; |
---|
37 | | - struct device_node *np = dev->of_node; |
---|
38 | | - int i; |
---|
39 | | - |
---|
40 | | - simple->num_clocks = count; |
---|
41 | | - |
---|
42 | | - if (!count) |
---|
43 | | - return 0; |
---|
44 | | - |
---|
45 | | - simple->clks = devm_kcalloc(dev, simple->num_clocks, |
---|
46 | | - sizeof(struct clk *), GFP_KERNEL); |
---|
47 | | - if (!simple->clks) |
---|
48 | | - return -ENOMEM; |
---|
49 | | - |
---|
50 | | - for (i = 0; i < simple->num_clocks; i++) { |
---|
51 | | - struct clk *clk; |
---|
52 | | - int ret; |
---|
53 | | - |
---|
54 | | - clk = of_clk_get(np, i); |
---|
55 | | - if (IS_ERR(clk)) { |
---|
56 | | - while (--i >= 0) { |
---|
57 | | - clk_disable_unprepare(simple->clks[i]); |
---|
58 | | - clk_put(simple->clks[i]); |
---|
59 | | - } |
---|
60 | | - return PTR_ERR(clk); |
---|
61 | | - } |
---|
62 | | - |
---|
63 | | - ret = clk_prepare_enable(clk); |
---|
64 | | - if (ret < 0) { |
---|
65 | | - while (--i >= 0) { |
---|
66 | | - clk_disable_unprepare(simple->clks[i]); |
---|
67 | | - clk_put(simple->clks[i]); |
---|
68 | | - } |
---|
69 | | - clk_put(clk); |
---|
70 | | - |
---|
71 | | - return ret; |
---|
72 | | - } |
---|
73 | | - |
---|
74 | | - simple->clks[i] = clk; |
---|
75 | | - } |
---|
76 | | - |
---|
77 | | - return 0; |
---|
78 | | -} |
---|
79 | 32 | |
---|
80 | 33 | static int dwc3_of_simple_probe(struct platform_device *pdev) |
---|
81 | 34 | { |
---|
.. | .. |
---|
84 | 37 | struct device_node *np = dev->of_node; |
---|
85 | 38 | |
---|
86 | 39 | int ret; |
---|
87 | | - int i; |
---|
88 | | - bool shared_resets = false; |
---|
89 | 40 | |
---|
90 | 41 | simple = devm_kzalloc(dev, sizeof(*simple), GFP_KERNEL); |
---|
91 | 42 | if (!simple) |
---|
.. | .. |
---|
101 | 52 | if (of_device_is_compatible(np, "rockchip,rk3399-dwc3")) |
---|
102 | 53 | simple->need_reset = true; |
---|
103 | 54 | |
---|
104 | | - if (of_device_is_compatible(np, "amlogic,meson-axg-dwc3") || |
---|
105 | | - of_device_is_compatible(np, "amlogic,meson-gxl-dwc3")) { |
---|
106 | | - shared_resets = true; |
---|
107 | | - simple->pulse_resets = true; |
---|
108 | | - } |
---|
109 | | - |
---|
110 | | - simple->resets = of_reset_control_array_get(np, shared_resets, true); |
---|
| 55 | + simple->resets = of_reset_control_array_get(np, false, true, |
---|
| 56 | + true); |
---|
111 | 57 | if (IS_ERR(simple->resets)) { |
---|
112 | 58 | ret = PTR_ERR(simple->resets); |
---|
113 | 59 | dev_err(dev, "failed to get device resets, err=%d\n", ret); |
---|
114 | 60 | return ret; |
---|
115 | 61 | } |
---|
116 | 62 | |
---|
117 | | - if (simple->pulse_resets) { |
---|
118 | | - ret = reset_control_reset(simple->resets); |
---|
119 | | - if (ret) |
---|
120 | | - goto err_resetc_put; |
---|
121 | | - } else { |
---|
122 | | - ret = reset_control_deassert(simple->resets); |
---|
123 | | - if (ret) |
---|
124 | | - goto err_resetc_put; |
---|
125 | | - } |
---|
| 63 | + ret = reset_control_deassert(simple->resets); |
---|
| 64 | + if (ret) |
---|
| 65 | + goto err_resetc_put; |
---|
126 | 66 | |
---|
127 | | - ret = dwc3_of_simple_clk_init(simple, of_count_phandle_with_args(np, |
---|
128 | | - "clocks", "#clock-cells")); |
---|
| 67 | + ret = clk_bulk_get_all(simple->dev, &simple->clks); |
---|
| 68 | + if (ret < 0) |
---|
| 69 | + goto err_resetc_assert; |
---|
| 70 | + |
---|
| 71 | + simple->num_clocks = ret; |
---|
| 72 | + ret = clk_bulk_prepare_enable(simple->num_clocks, simple->clks); |
---|
129 | 73 | if (ret) |
---|
130 | 74 | goto err_resetc_assert; |
---|
131 | 75 | |
---|
132 | 76 | ret = of_platform_populate(np, NULL, NULL, dev); |
---|
133 | | - if (ret) { |
---|
134 | | - for (i = 0; i < simple->num_clocks; i++) { |
---|
135 | | - clk_disable_unprepare(simple->clks[i]); |
---|
136 | | - clk_put(simple->clks[i]); |
---|
137 | | - } |
---|
138 | | - |
---|
139 | | - goto err_resetc_assert; |
---|
140 | | - } |
---|
| 77 | + if (ret) |
---|
| 78 | + goto err_clk_put; |
---|
141 | 79 | |
---|
142 | 80 | pm_runtime_set_active(dev); |
---|
143 | 81 | pm_runtime_enable(dev); |
---|
.. | .. |
---|
145 | 83 | |
---|
146 | 84 | return 0; |
---|
147 | 85 | |
---|
| 86 | +err_clk_put: |
---|
| 87 | + clk_bulk_disable_unprepare(simple->num_clocks, simple->clks); |
---|
| 88 | + clk_bulk_put_all(simple->num_clocks, simple->clks); |
---|
| 89 | + |
---|
148 | 90 | err_resetc_assert: |
---|
149 | | - if (!simple->pulse_resets) |
---|
150 | | - reset_control_assert(simple->resets); |
---|
| 91 | + reset_control_assert(simple->resets); |
---|
151 | 92 | |
---|
152 | 93 | err_resetc_put: |
---|
153 | 94 | reset_control_put(simple->resets); |
---|
154 | 95 | return ret; |
---|
155 | 96 | } |
---|
156 | 97 | |
---|
157 | | -static int dwc3_of_simple_remove(struct platform_device *pdev) |
---|
| 98 | +static void __dwc3_of_simple_teardown(struct dwc3_of_simple *simple) |
---|
158 | 99 | { |
---|
159 | | - struct dwc3_of_simple *simple = platform_get_drvdata(pdev); |
---|
160 | | - struct device *dev = &pdev->dev; |
---|
161 | | - int i; |
---|
| 100 | + of_platform_depopulate(simple->dev); |
---|
162 | 101 | |
---|
163 | | - of_platform_depopulate(dev); |
---|
164 | | - |
---|
165 | | - for (i = 0; i < simple->num_clocks; i++) { |
---|
166 | | - clk_disable_unprepare(simple->clks[i]); |
---|
167 | | - clk_put(simple->clks[i]); |
---|
168 | | - } |
---|
| 102 | + clk_bulk_disable_unprepare(simple->num_clocks, simple->clks); |
---|
| 103 | + clk_bulk_put_all(simple->num_clocks, simple->clks); |
---|
169 | 104 | simple->num_clocks = 0; |
---|
170 | 105 | |
---|
171 | | - if (!simple->pulse_resets) |
---|
172 | | - reset_control_assert(simple->resets); |
---|
| 106 | + reset_control_assert(simple->resets); |
---|
173 | 107 | |
---|
174 | 108 | reset_control_put(simple->resets); |
---|
175 | 109 | |
---|
176 | | - pm_runtime_disable(dev); |
---|
177 | | - pm_runtime_put_noidle(dev); |
---|
178 | | - pm_runtime_set_suspended(dev); |
---|
| 110 | + pm_runtime_disable(simple->dev); |
---|
| 111 | + pm_runtime_put_noidle(simple->dev); |
---|
| 112 | + pm_runtime_set_suspended(simple->dev); |
---|
| 113 | +} |
---|
| 114 | + |
---|
| 115 | +static int dwc3_of_simple_remove(struct platform_device *pdev) |
---|
| 116 | +{ |
---|
| 117 | + struct dwc3_of_simple *simple = platform_get_drvdata(pdev); |
---|
| 118 | + |
---|
| 119 | + __dwc3_of_simple_teardown(simple); |
---|
179 | 120 | |
---|
180 | 121 | return 0; |
---|
| 122 | +} |
---|
| 123 | + |
---|
| 124 | +static void dwc3_of_simple_shutdown(struct platform_device *pdev) |
---|
| 125 | +{ |
---|
| 126 | + struct dwc3_of_simple *simple = platform_get_drvdata(pdev); |
---|
| 127 | + |
---|
| 128 | + __dwc3_of_simple_teardown(simple); |
---|
181 | 129 | } |
---|
182 | 130 | |
---|
183 | 131 | static int __maybe_unused dwc3_of_simple_runtime_suspend(struct device *dev) |
---|
184 | 132 | { |
---|
185 | 133 | struct dwc3_of_simple *simple = dev_get_drvdata(dev); |
---|
186 | | - int i; |
---|
187 | 134 | |
---|
188 | | - for (i = 0; i < simple->num_clocks; i++) |
---|
189 | | - clk_disable(simple->clks[i]); |
---|
| 135 | + clk_bulk_disable(simple->num_clocks, simple->clks); |
---|
190 | 136 | |
---|
191 | 137 | return 0; |
---|
192 | 138 | } |
---|
.. | .. |
---|
194 | 140 | static int __maybe_unused dwc3_of_simple_runtime_resume(struct device *dev) |
---|
195 | 141 | { |
---|
196 | 142 | struct dwc3_of_simple *simple = dev_get_drvdata(dev); |
---|
197 | | - int ret; |
---|
198 | | - int i; |
---|
199 | 143 | |
---|
200 | | - for (i = 0; i < simple->num_clocks; i++) { |
---|
201 | | - ret = clk_enable(simple->clks[i]); |
---|
202 | | - if (ret < 0) { |
---|
203 | | - while (--i >= 0) |
---|
204 | | - clk_disable(simple->clks[i]); |
---|
205 | | - return ret; |
---|
206 | | - } |
---|
207 | | - } |
---|
208 | | - |
---|
209 | | - return 0; |
---|
| 144 | + return clk_bulk_enable(simple->num_clocks, simple->clks); |
---|
210 | 145 | } |
---|
211 | 146 | |
---|
212 | 147 | static int __maybe_unused dwc3_of_simple_suspend(struct device *dev) |
---|
.. | .. |
---|
240 | 175 | { .compatible = "xlnx,zynqmp-dwc3" }, |
---|
241 | 176 | { .compatible = "cavium,octeon-7130-usb-uctl" }, |
---|
242 | 177 | { .compatible = "sprd,sc9860-dwc3" }, |
---|
243 | | - { .compatible = "amlogic,meson-axg-dwc3" }, |
---|
244 | | - { .compatible = "amlogic,meson-gxl-dwc3" }, |
---|
245 | 178 | { .compatible = "allwinner,sun50i-h6-dwc3" }, |
---|
246 | 179 | { .compatible = "hisilicon,hi3670-dwc3" }, |
---|
| 180 | + { .compatible = "intel,keembay-dwc3" }, |
---|
247 | 181 | { /* Sentinel */ } |
---|
248 | 182 | }; |
---|
249 | 183 | MODULE_DEVICE_TABLE(of, of_dwc3_simple_match); |
---|
.. | .. |
---|
251 | 185 | static struct platform_driver dwc3_of_simple_driver = { |
---|
252 | 186 | .probe = dwc3_of_simple_probe, |
---|
253 | 187 | .remove = dwc3_of_simple_remove, |
---|
| 188 | + .shutdown = dwc3_of_simple_shutdown, |
---|
254 | 189 | .driver = { |
---|
255 | 190 | .name = "dwc3-of-simple", |
---|
256 | 191 | .of_match_table = of_dwc3_simple_match, |
---|