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