1 | // SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) |
2 | // Copyright(c) 2015-2023 Intel Corporation. All rights reserved. |
3 | |
4 | #include <linux/acpi.h> |
5 | #include <linux/soundwire/sdw_registers.h> |
6 | #include <linux/soundwire/sdw.h> |
7 | #include <linux/soundwire/sdw_intel.h> |
8 | #include "cadence_master.h" |
9 | #include "bus.h" |
10 | #include "intel.h" |
11 | |
12 | int intel_start_bus(struct sdw_intel *sdw) |
13 | { |
14 | struct device *dev = sdw->cdns.dev; |
15 | struct sdw_cdns *cdns = &sdw->cdns; |
16 | struct sdw_bus *bus = &cdns->bus; |
17 | int ret; |
18 | |
19 | /* |
20 | * follow recommended programming flows to avoid timeouts when |
21 | * gsync is enabled |
22 | */ |
23 | if (bus->multi_link) |
24 | sdw_intel_sync_arm(sdw); |
25 | |
26 | ret = sdw_cdns_init(cdns); |
27 | if (ret < 0) { |
28 | dev_err(dev, "%s: unable to initialize Cadence IP: %d\n" , __func__, ret); |
29 | return ret; |
30 | } |
31 | |
32 | sdw_cdns_config_update(cdns); |
33 | |
34 | if (bus->multi_link) { |
35 | ret = sdw_intel_sync_go(sdw); |
36 | if (ret < 0) { |
37 | dev_err(dev, "%s: sync go failed: %d\n" , __func__, ret); |
38 | return ret; |
39 | } |
40 | } |
41 | |
42 | ret = sdw_cdns_config_update_set_wait(cdns); |
43 | if (ret < 0) { |
44 | dev_err(dev, "%s: CONFIG_UPDATE BIT still set\n" , __func__); |
45 | return ret; |
46 | } |
47 | |
48 | ret = sdw_cdns_exit_reset(cdns); |
49 | if (ret < 0) { |
50 | dev_err(dev, "%s: unable to exit bus reset sequence: %d\n" , __func__, ret); |
51 | return ret; |
52 | } |
53 | |
54 | ret = sdw_cdns_enable_interrupt(cdns, state: true); |
55 | if (ret < 0) { |
56 | dev_err(dev, "%s: cannot enable interrupts: %d\n" , __func__, ret); |
57 | return ret; |
58 | } |
59 | |
60 | sdw_cdns_check_self_clearing_bits(cdns, string: __func__, |
61 | initial_delay: true, INTEL_MASTER_RESET_ITERATIONS); |
62 | |
63 | return 0; |
64 | } |
65 | |
66 | int intel_start_bus_after_reset(struct sdw_intel *sdw) |
67 | { |
68 | struct device *dev = sdw->cdns.dev; |
69 | struct sdw_cdns *cdns = &sdw->cdns; |
70 | struct sdw_bus *bus = &cdns->bus; |
71 | bool clock_stop0; |
72 | int status; |
73 | int ret; |
74 | |
75 | /* |
76 | * An exception condition occurs for the CLK_STOP_BUS_RESET |
77 | * case if one or more masters remain active. In this condition, |
78 | * all the masters are powered on for they are in the same power |
79 | * domain. Master can preserve its context for clock stop0, so |
80 | * there is no need to clear slave status and reset bus. |
81 | */ |
82 | clock_stop0 = sdw_cdns_is_clock_stop(cdns: &sdw->cdns); |
83 | |
84 | if (!clock_stop0) { |
85 | |
86 | /* |
87 | * make sure all Slaves are tagged as UNATTACHED and |
88 | * provide reason for reinitialization |
89 | */ |
90 | |
91 | status = SDW_UNATTACH_REQUEST_MASTER_RESET; |
92 | sdw_clear_slave_status(bus, request: status); |
93 | |
94 | /* |
95 | * follow recommended programming flows to avoid |
96 | * timeouts when gsync is enabled |
97 | */ |
98 | if (bus->multi_link) |
99 | sdw_intel_sync_arm(sdw); |
100 | |
101 | /* |
102 | * Re-initialize the IP since it was powered-off |
103 | */ |
104 | sdw_cdns_init(cdns: &sdw->cdns); |
105 | |
106 | } else { |
107 | ret = sdw_cdns_enable_interrupt(cdns, state: true); |
108 | if (ret < 0) { |
109 | dev_err(dev, "cannot enable interrupts during resume\n" ); |
110 | return ret; |
111 | } |
112 | } |
113 | |
114 | ret = sdw_cdns_clock_restart(cdns, bus_reset: !clock_stop0); |
115 | if (ret < 0) { |
116 | dev_err(dev, "unable to restart clock during resume\n" ); |
117 | if (!clock_stop0) |
118 | sdw_cdns_enable_interrupt(cdns, state: false); |
119 | return ret; |
120 | } |
121 | |
122 | if (!clock_stop0) { |
123 | sdw_cdns_config_update(cdns); |
124 | |
125 | if (bus->multi_link) { |
126 | ret = sdw_intel_sync_go(sdw); |
127 | if (ret < 0) { |
128 | dev_err(sdw->cdns.dev, "sync go failed during resume\n" ); |
129 | return ret; |
130 | } |
131 | } |
132 | |
133 | ret = sdw_cdns_config_update_set_wait(cdns); |
134 | if (ret < 0) { |
135 | dev_err(dev, "%s: CONFIG_UPDATE BIT still set\n" , __func__); |
136 | return ret; |
137 | } |
138 | |
139 | ret = sdw_cdns_exit_reset(cdns); |
140 | if (ret < 0) { |
141 | dev_err(dev, "unable to exit bus reset sequence during resume\n" ); |
142 | return ret; |
143 | } |
144 | |
145 | ret = sdw_cdns_enable_interrupt(cdns, state: true); |
146 | if (ret < 0) { |
147 | dev_err(dev, "cannot enable interrupts during resume\n" ); |
148 | return ret; |
149 | } |
150 | |
151 | } |
152 | sdw_cdns_check_self_clearing_bits(cdns, string: __func__, initial_delay: true, INTEL_MASTER_RESET_ITERATIONS); |
153 | |
154 | return 0; |
155 | } |
156 | |
157 | void intel_check_clock_stop(struct sdw_intel *sdw) |
158 | { |
159 | struct device *dev = sdw->cdns.dev; |
160 | bool clock_stop0; |
161 | |
162 | clock_stop0 = sdw_cdns_is_clock_stop(cdns: &sdw->cdns); |
163 | if (!clock_stop0) |
164 | dev_err(dev, "%s: invalid configuration, clock was not stopped\n" , __func__); |
165 | } |
166 | |
167 | int intel_start_bus_after_clock_stop(struct sdw_intel *sdw) |
168 | { |
169 | struct device *dev = sdw->cdns.dev; |
170 | struct sdw_cdns *cdns = &sdw->cdns; |
171 | int ret; |
172 | |
173 | ret = sdw_cdns_clock_restart(cdns, bus_reset: false); |
174 | if (ret < 0) { |
175 | dev_err(dev, "%s: unable to restart clock: %d\n" , __func__, ret); |
176 | return ret; |
177 | } |
178 | |
179 | ret = sdw_cdns_enable_interrupt(cdns, state: true); |
180 | if (ret < 0) { |
181 | dev_err(dev, "%s: cannot enable interrupts: %d\n" , __func__, ret); |
182 | return ret; |
183 | } |
184 | |
185 | sdw_cdns_check_self_clearing_bits(cdns, string: __func__, initial_delay: true, INTEL_MASTER_RESET_ITERATIONS); |
186 | |
187 | return 0; |
188 | } |
189 | |
190 | int intel_stop_bus(struct sdw_intel *sdw, bool clock_stop) |
191 | { |
192 | struct device *dev = sdw->cdns.dev; |
193 | struct sdw_cdns *cdns = &sdw->cdns; |
194 | bool wake_enable = false; |
195 | int ret; |
196 | |
197 | if (clock_stop) { |
198 | ret = sdw_cdns_clock_stop(cdns, block_wake: true); |
199 | if (ret < 0) |
200 | dev_err(dev, "%s: cannot stop clock: %d\n" , __func__, ret); |
201 | else |
202 | wake_enable = true; |
203 | } |
204 | |
205 | ret = sdw_cdns_enable_interrupt(cdns, state: false); |
206 | if (ret < 0) { |
207 | dev_err(dev, "%s: cannot disable interrupts: %d\n" , __func__, ret); |
208 | return ret; |
209 | } |
210 | |
211 | ret = sdw_intel_link_power_down(sdw); |
212 | if (ret) { |
213 | dev_err(dev, "%s: Link power down failed: %d\n" , __func__, ret); |
214 | return ret; |
215 | } |
216 | |
217 | sdw_intel_shim_wake(sdw, wake_enable); |
218 | |
219 | return 0; |
220 | } |
221 | |
222 | /* |
223 | * bank switch routines |
224 | */ |
225 | |
226 | int intel_pre_bank_switch(struct sdw_intel *sdw) |
227 | { |
228 | struct sdw_cdns *cdns = &sdw->cdns; |
229 | struct sdw_bus *bus = &cdns->bus; |
230 | |
231 | /* Write to register only for multi-link */ |
232 | if (!bus->multi_link) |
233 | return 0; |
234 | |
235 | sdw_intel_sync_arm(sdw); |
236 | |
237 | return 0; |
238 | } |
239 | |
240 | int intel_post_bank_switch(struct sdw_intel *sdw) |
241 | { |
242 | struct sdw_cdns *cdns = &sdw->cdns; |
243 | struct sdw_bus *bus = &cdns->bus; |
244 | int ret = 0; |
245 | |
246 | /* Write to register only for multi-link */ |
247 | if (!bus->multi_link) |
248 | return 0; |
249 | |
250 | mutex_lock(sdw->link_res->shim_lock); |
251 | |
252 | /* |
253 | * post_bank_switch() ops is called from the bus in loop for |
254 | * all the Masters in the steam with the expectation that |
255 | * we trigger the bankswitch for the only first Master in the list |
256 | * and do nothing for the other Masters |
257 | * |
258 | * So, set the SYNCGO bit only if CMDSYNC bit is set for any Master. |
259 | */ |
260 | if (sdw_intel_sync_check_cmdsync_unlocked(sdw)) |
261 | ret = sdw_intel_sync_go_unlocked(sdw); |
262 | |
263 | mutex_unlock(lock: sdw->link_res->shim_lock); |
264 | |
265 | if (ret < 0) |
266 | dev_err(sdw->cdns.dev, "Post bank switch failed: %d\n" , ret); |
267 | |
268 | return ret; |
269 | } |
270 | |