Skip to content

Commit

Permalink
fsi: occ: Use a large buffer for responses
Browse files Browse the repository at this point in the history
Allocate a large buffer for each OCC to handle response data. This
removes memory allocation during an operation, and also allows for
the maximum amount of SBE FFDC.

Previously for the putsram and attn commands, only 32 words would have
been available, and for getsram, only up to the size of the transfer.
SBE FFDC might be up to 8Kb.

The SBE interface expects data to be specified in units of words (4
bytes), defined as OCC_MAX_RESP_WORDS.

This change allows the full FFDC capture to be implemented, where before
it was not available.

Signed-off-by: Eddie James <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
Signed-off-by: Joel Stanley <[email protected]>
  • Loading branch information
Eddie James authored and shenki committed Oct 21, 2021
1 parent 908dbf0 commit 008d382
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 66 deletions.
110 changes: 44 additions & 66 deletions drivers/fsi/fsi-occ.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/miscdevice.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/fsi-occ.h>
Expand All @@ -33,13 +34,6 @@

#define OCC_P10_SRAM_MODE 0x58 /* Normal mode, OCB channel 2 */

/*
* Assume we don't have much FFDC, if we do we'll overflow and
* fail the command. This needs to be big enough for simple
* commands as well.
*/
#define OCC_SBE_STATUS_WORDS 32

#define OCC_TIMEOUT_MS 1000
#define OCC_CMD_IN_PRG_WAIT_MS 50

Expand All @@ -51,6 +45,7 @@ struct occ {
char name[32];
int idx;
u8 sequence_number;
void *buffer;
enum versions version;
struct miscdevice mdev;
struct mutex occ_lock;
Expand Down Expand Up @@ -241,8 +236,10 @@ static int occ_verify_checksum(struct occ *occ, struct occ_response *resp,
static int occ_getsram(struct occ *occ, u32 offset, void *data, ssize_t len)
{
u32 data_len = ((len + 7) / 8) * 8; /* must be multiples of 8 B */
size_t cmd_len, resp_len, resp_data_len;
__be32 *resp, cmd[6];
size_t cmd_len, resp_data_len;
size_t resp_len = OCC_MAX_RESP_WORDS;
__be32 *resp = occ->buffer;
__be32 cmd[6];
int idx = 0, rc;

/*
Expand All @@ -269,19 +266,19 @@ static int occ_getsram(struct occ *occ, u32 offset, void *data, ssize_t len)
cmd[1] = cpu_to_be32(SBEFIFO_CMD_GET_OCC_SRAM);
cmd[4 + idx] = cpu_to_be32(data_len);

resp_len = (data_len >> 2) + OCC_SBE_STATUS_WORDS;
resp = kzalloc(resp_len << 2, GFP_KERNEL);
if (!resp)
return -ENOMEM;

rc = sbefifo_submit(occ->sbefifo, cmd, cmd_len, resp, &resp_len);
if (rc)
goto free;
return rc;

rc = sbefifo_parse_status(occ->sbefifo, SBEFIFO_CMD_GET_OCC_SRAM,
resp, resp_len, &resp_len);
if (rc)
goto free;
if (rc > 0) {
dev_err(occ->dev, "SRAM read returned failure status: %08x\n",
rc);
return -EBADMSG;
} else if (rc) {
return rc;
}

resp_data_len = be32_to_cpu(resp[resp_len - 1]);
if (resp_data_len != data_len) {
Expand All @@ -292,39 +289,21 @@ static int occ_getsram(struct occ *occ, u32 offset, void *data, ssize_t len)
memcpy(data, resp, len);
}

free:
/* Convert positive SBEI status */
if (rc > 0) {
dev_err(occ->dev, "SRAM read returned failure status: %08x\n",
rc);
rc = -EBADMSG;
}

kfree(resp);
return rc;
}

static int occ_putsram(struct occ *occ, const void *data, ssize_t len,
u8 seq_no, u16 checksum)
{
size_t cmd_len, buf_len, resp_len, resp_data_len;
u32 data_len = ((len + 7) / 8) * 8; /* must be multiples of 8 B */
__be32 *buf;
size_t cmd_len, resp_data_len;
size_t resp_len = OCC_MAX_RESP_WORDS;
__be32 *buf = occ->buffer;
u8 *byte_buf;
int idx = 0, rc;

cmd_len = (occ->version == occ_p10) ? 6 : 5;

/*
* We use the same buffer for command and response, make
* sure it's big enough
*/
resp_len = OCC_SBE_STATUS_WORDS;
cmd_len += data_len >> 2;
buf_len = max(cmd_len, resp_len);
buf = kzalloc(buf_len << 2, GFP_KERNEL);
if (!buf)
return -ENOMEM;

/*
* Magic sequence to do SBE putsram command. SBE will transfer
Expand Down Expand Up @@ -361,12 +340,17 @@ static int occ_putsram(struct occ *occ, const void *data, ssize_t len,

rc = sbefifo_submit(occ->sbefifo, buf, cmd_len, buf, &resp_len);
if (rc)
goto free;
return rc;

rc = sbefifo_parse_status(occ->sbefifo, SBEFIFO_CMD_PUT_OCC_SRAM,
buf, resp_len, &resp_len);
if (rc)
goto free;
if (rc > 0) {
dev_err(occ->dev, "SRAM write returned failure status: %08x\n",
rc);
return -EBADMSG;
} else if (rc) {
return rc;
}

if (resp_len != 1) {
dev_err(occ->dev, "SRAM write response length invalid: %zd\n",
Expand All @@ -382,27 +366,16 @@ static int occ_putsram(struct occ *occ, const void *data, ssize_t len,
}
}

free:
/* Convert positive SBEI status */
if (rc > 0) {
dev_err(occ->dev, "SRAM write returned failure status: %08x\n",
rc);
rc = -EBADMSG;
}

kfree(buf);
return rc;
}

static int occ_trigger_attn(struct occ *occ)
{
__be32 buf[OCC_SBE_STATUS_WORDS];
size_t cmd_len, resp_len, resp_data_len;
__be32 *buf = occ->buffer;
size_t cmd_len, resp_data_len;
size_t resp_len = OCC_MAX_RESP_WORDS;
int idx = 0, rc;

BUILD_BUG_ON(OCC_SBE_STATUS_WORDS < 8);
resp_len = OCC_SBE_STATUS_WORDS;

switch (occ->version) {
default:
case occ_p9:
Expand All @@ -427,12 +400,17 @@ static int occ_trigger_attn(struct occ *occ)

rc = sbefifo_submit(occ->sbefifo, buf, cmd_len, buf, &resp_len);
if (rc)
goto error;
return rc;

rc = sbefifo_parse_status(occ->sbefifo, SBEFIFO_CMD_PUT_OCC_SRAM,
buf, resp_len, &resp_len);
if (rc)
goto error;
if (rc > 0) {
dev_err(occ->dev, "SRAM attn returned failure status: %08x\n",
rc);
return -EBADMSG;
} else if (rc) {
return rc;
}

if (resp_len != 1) {
dev_err(occ->dev, "SRAM attn response length invalid: %zd\n",
Expand All @@ -448,14 +426,6 @@ static int occ_trigger_attn(struct occ *occ)
}
}

error:
/* Convert positive SBEI status */
if (rc > 0) {
dev_err(occ->dev, "SRAM attn returned failure status: %08x\n",
rc);
rc = -EBADMSG;
}

return rc;
}

Expand Down Expand Up @@ -590,6 +560,11 @@ static int occ_probe(struct platform_device *pdev)
if (!occ)
return -ENOMEM;

/* SBE words are always four bytes */
occ->buffer = kvmalloc(OCC_MAX_RESP_WORDS * 4, GFP_KERNEL);
if (!occ->buffer)
return -ENOMEM;

occ->version = (uintptr_t)of_device_get_match_data(dev);
occ->dev = dev;
occ->sbefifo = dev->parent;
Expand Down Expand Up @@ -625,6 +600,7 @@ static int occ_probe(struct platform_device *pdev)
if (rc) {
dev_err(dev, "failed to register miscdevice: %d\n", rc);
ida_simple_remove(&occ_ida, occ->idx);
kvfree(occ->buffer);
return rc;
}

Expand All @@ -640,6 +616,8 @@ static int occ_remove(struct platform_device *pdev)
{
struct occ *occ = platform_get_drvdata(pdev);

kvfree(occ->buffer);

misc_deregister(&occ->mdev);

device_for_each_child(&pdev->dev, NULL, occ_unregister_child);
Expand Down
2 changes: 2 additions & 0 deletions include/linux/fsi-occ.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ struct device;
#define OCC_RESP_CRIT_OCB 0xE3
#define OCC_RESP_CRIT_HW 0xE4

#define OCC_MAX_RESP_WORDS 2048

int fsi_occ_submit(struct device *dev, const void *request, size_t req_len,
void *response, size_t *resp_len);

Expand Down

0 comments on commit 008d382

Please sign in to comment.