Skip to content

Commit 2908ffa

Browse files
ambaruskrzk
authored andcommitted
firmware: exynos-acpm: check saved RX before bailing out on empty RX queue
When we're polling for responses and get a response that corresponds to another request, we save the RX data in order to drain the RX queue. If the response for the current request is not found in the request's iteration of the queue, or if the queue is empty, we must check whether the RX data was saved by a previous request when it drained the RX queue. We failed to check for already saved responses when the queue was empty, and requests could time out. Check saved RX before bailing out on empty RX queue. Fixes: a88927b ("firmware: add Exynos ACPM protocol driver") Reported-by: André Draszik <[email protected]> Signed-off-by: Tudor Ambarus <[email protected]> Reviewed-by: André Draszik <[email protected]> Tested-by: André Draszik <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Krzysztof Kozlowski <[email protected]>
1 parent 0af2f6b commit 2908ffa

File tree

1 file changed

+30
-14
lines changed

1 file changed

+30
-14
lines changed

drivers/firmware/samsung/exynos-acpm.c

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,29 @@ struct acpm_match_data {
184184
#define client_to_acpm_chan(c) container_of(c, struct acpm_chan, cl)
185185
#define handle_to_acpm_info(h) container_of(h, struct acpm_info, handle)
186186

187+
/**
188+
* acpm_get_saved_rx() - get the response if it was already saved.
189+
* @achan: ACPM channel info.
190+
* @xfer: reference to the transfer to get response for.
191+
* @tx_seqnum: xfer TX sequence number.
192+
*/
193+
static void acpm_get_saved_rx(struct acpm_chan *achan,
194+
const struct acpm_xfer *xfer, u32 tx_seqnum)
195+
{
196+
const struct acpm_rx_data *rx_data = &achan->rx_data[tx_seqnum - 1];
197+
u32 rx_seqnum;
198+
199+
if (!rx_data->response)
200+
return;
201+
202+
rx_seqnum = FIELD_GET(ACPM_PROTOCOL_SEQNUM, rx_data->cmd[0]);
203+
204+
if (rx_seqnum == tx_seqnum) {
205+
memcpy(xfer->rxd, rx_data->cmd, xfer->rxlen);
206+
clear_bit(rx_seqnum - 1, achan->bitmap_seqnum);
207+
}
208+
}
209+
187210
/**
188211
* acpm_get_rx() - get response from RX queue.
189212
* @achan: ACPM channel info.
@@ -204,15 +227,16 @@ static int acpm_get_rx(struct acpm_chan *achan, const struct acpm_xfer *xfer)
204227
rx_front = readl(achan->rx.front);
205228
i = readl(achan->rx.rear);
206229

207-
/* Bail out if RX is empty. */
208-
if (i == rx_front)
230+
tx_seqnum = FIELD_GET(ACPM_PROTOCOL_SEQNUM, xfer->txd[0]);
231+
232+
if (i == rx_front) {
233+
acpm_get_saved_rx(achan, xfer, tx_seqnum);
209234
return 0;
235+
}
210236

211237
base = achan->rx.base;
212238
mlen = achan->mlen;
213239

214-
tx_seqnum = FIELD_GET(ACPM_PROTOCOL_SEQNUM, xfer->txd[0]);
215-
216240
/* Drain RX queue. */
217241
do {
218242
/* Read RX seqnum. */
@@ -259,16 +283,8 @@ static int acpm_get_rx(struct acpm_chan *achan, const struct acpm_xfer *xfer)
259283
* If the response was not in this iteration of the queue, check if the
260284
* RX data was previously saved.
261285
*/
262-
rx_data = &achan->rx_data[tx_seqnum - 1];
263-
if (!rx_set && rx_data->response) {
264-
rx_seqnum = FIELD_GET(ACPM_PROTOCOL_SEQNUM,
265-
rx_data->cmd[0]);
266-
267-
if (rx_seqnum == tx_seqnum) {
268-
memcpy(xfer->rxd, rx_data->cmd, xfer->rxlen);
269-
clear_bit(rx_seqnum - 1, achan->bitmap_seqnum);
270-
}
271-
}
286+
if (!rx_set)
287+
acpm_get_saved_rx(achan, xfer, tx_seqnum);
272288

273289
return 0;
274290
}

0 commit comments

Comments
 (0)