[PATCH v2 09/15] misc: nnpi: Process device response messages

From: Guy Zadicario
Date: Thu May 13 2021 - 05:00:53 EST


The nnpdev_process_messages function handles all messages coming
from a NNP-I device. Based on an opcode field attached to each message,
it calls the correct response processing handler. The function is
called from the NNP-I device driver, from a threaded interrupt handler, when
responses arrive in the HW response queue.

Signed-off-by: Guy Zadicario <guy.zadicario@xxxxxxxxx>
Reviewed-by: Alexander Shishkin <alexander.shishkin@xxxxxxxxxxxxxxx>
---
drivers/misc/intel-nnpi/device.c | 136 +++++++++++++++++++++++++++++++++++++
drivers/misc/intel-nnpi/device.h | 10 +++
drivers/misc/intel-nnpi/nnp_pcie.c | 3 +
3 files changed, 149 insertions(+)

diff --git a/drivers/misc/intel-nnpi/device.c b/drivers/misc/intel-nnpi/device.c
index f4fc975..2e6ab82 100644
--- a/drivers/misc/intel-nnpi/device.c
+++ b/drivers/misc/intel-nnpi/device.c
@@ -23,6 +23,142 @@ bool nnpdev_no_devices(void)
return ida_is_empty(&dev_ida);
}

+/**
+ * handle_bios_protocol() - process response coming from card's BIOS.
+ * @nnpdev: The nnp device
+ * @msgbuf: pointer to response message content
+ * @avail_qwords: number of 64-bit units available in @msgbuf
+ *
+ * IPC protocol with card's BIOS may have different response sizes.
+ * @avail_qwords is the number of 64-bit units available in @msgbuf buffer.
+ * If the actual response size is larger then available data in the buffer,
+ * the function returns 0 to indicate that this is a partial response. Otherwise
+ * the actual response size is returned (in units of qwords).
+ *
+ * Return: 0 if @msgbuf contains a partial response otherwise the number of
+ * qwords of the response in @msgbuf.
+ */
+static int handle_bios_protocol(struct nnp_device *nnpdev, const u64 *msgbuf,
+ int avail_qwords)
+{
+ int msg_size, msg_qwords;
+
+ msg_size = FIELD_GET(NNP_C2H_BIOS_PROTOCOL_TYPE_MASK, msgbuf[0]);
+
+ /* The +1 is because size field does not include header */
+ msg_qwords = DIV_ROUND_UP(msg_size, 8) + 1;
+
+ if (msg_qwords > avail_qwords)
+ return 0;
+
+ return msg_qwords;
+}
+
+typedef int (*response_handler)(struct nnp_device *nnpdev, const u64 *msgbuf,
+ int avail_qwords);
+
+static response_handler resp_handlers[NNP_IPC_C2H_OPCODE_LAST + 1] = {
+ [NNP_IPC_C2H_OP_BIOS_PROTOCOL] = handle_bios_protocol
+};
+
+/**
+ * nnpdev_process_messages() - process response messages from nnpi device
+ * @nnpdev: The nnp device
+ * @hw_msg: pointer to response message content
+ * @hw_nof_msg: number of 64-bit units available in hw_msg buffer.
+ *
+ * This function is called from the PCIe device driver when response messages
+ * are arrived in the HWQ. It is called in sequence, should not be re-entrant.
+ * The function may not block !
+ */
+void nnpdev_process_messages(struct nnp_device *nnpdev, u64 *hw_msg,
+ unsigned int hw_nof_msg)
+{
+ int j = 0;
+ int msg_size;
+ u64 *msg;
+ unsigned int nof_msg;
+ bool fatal_protocol_error = false;
+
+ /* ignore any response if protocol error detected */
+ if ((nnpdev->state & NNP_DEVICE_PROTOCOL_ERROR) != 0)
+ return;
+
+ /*
+ * If we have pending messages from previous round
+ * copy the new messages to the pending list and process
+ * the pending list.
+ * otherwise process the messages received from HW directly
+ */
+ msg = hw_msg;
+ nof_msg = hw_nof_msg;
+ if (nnpdev->response_num_msgs > 0) {
+ /*
+ * Check to prevent response buffer overrun.
+ * This should never happen since the buffer is twice
+ * the size of the HW response queue. This check is
+ * for safety and debug purposes.
+ */
+ if (hw_nof_msg + nnpdev->response_num_msgs >=
+ NNP_DEVICE_RESPONSE_BUFFER_LEN) {
+ dev_dbg(nnpdev->dev,
+ "device response buffer would overrun: %d + %d !!\n",
+ nnpdev->response_num_msgs, hw_nof_msg);
+ return;
+ }
+
+ memcpy(&nnpdev->response_buf[nnpdev->response_num_msgs], hw_msg,
+ hw_nof_msg * sizeof(u64));
+ msg = nnpdev->response_buf;
+ nof_msg = nnpdev->response_num_msgs + hw_nof_msg;
+ }
+
+ /*
+ * loop for each message
+ */
+ do {
+ int op_code = FIELD_GET(NNP_C2H_OP_MASK, msg[j]);
+ response_handler handler;
+
+ /* opcodes above OP_BIOS_PROTOCOL are not yet supported */
+ if (op_code > NNP_IPC_C2H_OP_BIOS_PROTOCOL) {
+ fatal_protocol_error = true;
+ break;
+ }
+
+ /* dispatch the message request */
+ handler = resp_handlers[op_code];
+ if (!handler) {
+ /* Should not happen! */
+ dev_dbg(nnpdev->dev,
+ "Unknown response opcode received %d (0x%llx)\n",
+ op_code, msg[j]);
+ fatal_protocol_error = true;
+ break;
+ }
+
+ msg_size = (*handler)(nnpdev, &msg[j], (nof_msg - j));
+
+ j += msg_size;
+ } while (j < nof_msg || !msg_size);
+
+ if (fatal_protocol_error)
+ nnpdev->state |= NNP_DEVICE_PROTOCOL_ERROR;
+
+ /*
+ * If unprocessed messages left, copy it to the pending messages buffer
+ * for the next time this function will be called.
+ */
+ if (j < nof_msg) {
+ memcpy(&nnpdev->response_buf[0], &msg[j],
+ (nof_msg - j) * sizeof(u64));
+ nnpdev->response_num_msgs = nof_msg - j;
+ } else {
+ nnpdev->response_num_msgs = 0;
+ }
+}
+EXPORT_SYMBOL(nnpdev_process_messages);
+
static void send_sysinfo_request_to_bios(struct nnp_device *nnpdev)
{
u64 cmd[3];
diff --git a/drivers/misc/intel-nnpi/device.h b/drivers/misc/intel-nnpi/device.h
index 3745f5c..b83f67a 100644
--- a/drivers/misc/intel-nnpi/device.h
+++ b/drivers/misc/intel-nnpi/device.h
@@ -48,6 +48,9 @@
#define NNP_DEVICE_CORRUPTED_BOOT_IMAGE BIT(30)
#define NNP_DEVICE_ERROR_MASK GENMASK(31, 16)

+#define NNP_DEVICE_RESPONSE_FIFO_LEN 16
+#define NNP_DEVICE_RESPONSE_BUFFER_LEN (NNP_DEVICE_RESPONSE_FIFO_LEN * 2)
+
/**
* struct nnp_device - structure for NNP-I device info
* @ops: device operations implemented by the underlying device driver
@@ -61,6 +64,8 @@
* @lock: protects accesses to @state
* @is_recovery_bios: true if device has booted from the recovery bios flash
* @boot_image_loaded: true if boot image load has started
+ * @response_buf: buffer of device response messages arrived from "pci" layer.
+ * @response_num_msgs: number of qwords available in @response_buf
* @bios_system_info_dma_addr: dma page allocated for bios system info.
* @bios_system_info: virtual pointer to bios system info page
* @bios_version_str: the device's started bios version string
@@ -83,6 +88,9 @@ struct nnp_device {
bool is_recovery_bios;
bool boot_image_loaded;

+ u64 response_buf[NNP_DEVICE_RESPONSE_BUFFER_LEN];
+ unsigned int response_num_msgs;
+
dma_addr_t bios_system_info_dma_addr;
struct nnp_c2h_system_info *bios_system_info;
char bios_version_str[NNP_BIOS_VERSION_LEN];
@@ -118,6 +126,8 @@ int nnpdev_init(struct nnp_device *nnpdev, struct device *dev,
void nnpdev_destroy(struct nnp_device *nnpdev);
void nnpdev_card_doorbell_value_changed(struct nnp_device *nnpdev,
u32 doorbell_val);
+void nnpdev_process_messages(struct nnp_device *nnpdev, u64 *hw_msg,
+ unsigned int hw_nof_msg);

/*
* Framework internal functions (not exported)
diff --git a/drivers/misc/intel-nnpi/nnp_pcie.c b/drivers/misc/intel-nnpi/nnp_pcie.c
index 3840a53..4c9b4c6 100644
--- a/drivers/misc/intel-nnpi/nnp_pcie.c
+++ b/drivers/misc/intel-nnpi/nnp_pcie.c
@@ -150,6 +150,9 @@ static void nnp_process_commands(struct nnp_pci *nnp_pci)
response_pci_control |= FIELD_PREP(RESPQ_READ_PTR_MASK, read_pointer);
nnp_mmio_write(nnp_pci, ELBI_RESPONSE_PCI_CONTROL,
response_pci_control);
+
+ nnpdev_process_messages(&nnp_pci->nnpdev, nnp_pci->response_buf,
+ avail_slots);
}

static void mask_all_interrupts(struct nnp_pci *nnp_pci)
--
1.8.3.1