blob: 0ed5c7a9d8c474d1d479aac12ab7760910b36511 [file] [log] [blame]
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* GXP user command interface.
*
* Copyright (C) 2022 Google LLC
*/
#ifndef __GXP_UCI_H__
#define __GXP_UCI_H__
#include <linux/kref.h>
#include <linux/kthread.h>
#include <gcip/gcip-fence-array.h>
#include <gcip/gcip-fence.h>
#include <gcip/gcip-mailbox.h>
#include "gxp-client.h"
#include "gxp-internal.h"
#include "gxp-mailbox.h"
#include "gxp-vd.h"
#include "gxp.h"
#define UCI_RESOURCE_ID 0
struct gxp_mcu;
/* Command/Response Structures */
/* Size of `gxp_uci_type` should be u8 to match FW */
enum gxp_uci_type {
/* The command containing executable runtime command. */
CORE_COMMAND = 0,
/*
* Empty command that FW will simply respond to this command.
* Its purpose is to overcome the limitation of the number of in/out sync fences.
* From the perspective of KD, the communication logic with FW should be the same with
* RUNTIME_COMMAND.
*/
NULL_COMMAND = 1,
} __packed;
struct gxp_uci_wakelock_command_params {
/* DVFS operating point of DSP cores */
uint8_t dsp_operating_point;
/* DVFS operating point of memory */
uint8_t memory_operating_point;
};
struct gxp_uci_core_command_params {
/* iova address of the app command */
uint64_t address;
/* size of the app command */
uint32_t size;
/* number of dsp cores required for this command */
uint8_t num_cores;
/* DVFS operating point of DSP cores */
uint8_t dsp_operating_point;
/* DVFS operating point of memory */
uint8_t memory_operating_point;
};
struct gxp_uci_command {
/* sequence number, should match the corresponding response */
uint32_t seq;
/* device address of the additional info */
uint32_t additional_info_address;
/* unique ID for each client that identifies client VM & security realm */
uint32_t client_id;
/* type of the command */
enum gxp_uci_type type;
/* reserved field */
uint8_t reserved;
/* size of the additional info */
uint16_t additional_info_size;
/* All possible command parameters */
union {
struct gxp_uci_core_command_params core_command_params;
struct gxp_uci_wakelock_command_params wakelock_command_params;
uint8_t opaque[48];
};
};
/**
* struct gxp_uci_command_work - The callback and work object to carry data for a UCI command.
* @cb: The embedded DMA callback.
* @node: The list node used to add to the client.
* @fence: The fence that the callback is added to.
* @client: The client which request the UCI command.
* @cmd_seq: The specified sequence number used for the uci command.
* @flags: Same as gxp_mailbox_uci_command_ioctl.
* @opaque: Same as gxp_mailbox_uci_command_ioctl.
* @timeout_ms: Same as gxp_mailbox_uci_command_ioctl.
* @in_fences: Same as gxp_mailbox_uci_command_ioctl.
* @out_fences: Same as gxp_mailbox_uci_command_ioctl.
*/
struct gxp_uci_cmd_work {
struct dma_fence_cb cb;
struct list_head node;
struct dma_fence *fence;
struct gxp_client *client;
u64 cmd_seq;
u32 flags;
u8 opaque[GXP_UCI_CMD_OPAQUE_SIZE];
u32 timeout_ms;
struct gcip_fence_array *in_fences;
struct gcip_fence_array *out_fences;
};
/*
* TODO(b/301608032): This additional_info must be generated by the litebuf once its C compiler
* is implemented. Before that we should statiaclly define compatible structures here.
*
* +----------------------------------------+ <- 0
* | struct gxp_uci_additional_info_header |
* +----------------------------------------+ <- root_offset
* | | \
* | | |
* | struct gxp_uci_additional_info_root | | -> object_size
* | | |
* | | /
* +----------------------------------------+ <- in_fences_offset
* | |
* | uint16_t in_fences[in_fences_size] |
* | |
* +----------------------------------------+ <- out_fences_offset
* | |
* | uint16_t out_fences[out_fences_size] |
* | |
* +----------------------------------------+ <- runtime_additional_info_offset
* | |
* | uint8_t runtime_additional_info[ |
* | runtime_additional_info_size |
* | ] |
* | |
* +----------------------------------------+ <- additional_info_size
*/
struct gxp_uci_additional_info_header {
/* Unique identifier to differentiate between multiple litebuf schemas. */
uint8_t identifier;
/* Unique version within a single schema. */
uint8_t version;
/* Reserved for alignment and future use. */
uint8_t reserved[6];
/* Pointer to the root object. */
uint64_t root_offset;
} __packed;
struct gxp_uci_additional_info_root {
/* The total size of this root object in bytes. */
uint32_t object_size;
/* The offset of the in_fences vector. */
int32_t in_fences_offset;
/* The number of elements in the in_fences vector. */
uint32_t in_fences_size;
/* The offset of the out_fences vector. */
int32_t out_fences_offset;
/* The number of elements in the out_fences vector. */
uint32_t out_fences_size;
/* Runtime specified timeout. */
uint32_t timeout_ms;
/* The offset of the runtime defined additional info buffer. */
int32_t runtime_additional_info_offset;
/* The size of the runtime defined additional info buffer in bytes. */
uint32_t runtime_additional_info_size;
} __packed;
struct gxp_uci_additional_info {
/* Header. */
struct gxp_uci_additional_info_header header;
/* Root. */
struct gxp_uci_additional_info_root root;
/* The pointer to in_fences of UCI command ioctl. */
uint16_t *in_fences;
/* The pointer to out_fences of UCI command ioctl. */
uint16_t *out_fences;
/* The pointer to runtime_additional_info of UCI command ioctl. */
uint8_t *runtime_additional_info;
};
struct gxp_uci_response {
/* sequence number, should match the corresponding command */
uint64_t seq;
/* unique ID for each client that identifies client VM & security realm*/
uint32_t client_id;
/* status code that tells the success or error. */
uint16_t code;
/* reserved field */
uint8_t reserved[2];
uint8_t opaque[16];
};
/*
* Wrapper struct for responses consumed by a thread other than the one which
* sent the command.
*/
struct gxp_uci_async_response {
/*
* List entry which will be inserted to the waiting queue of the vd.
* It will be pushed into the waiting queue when the response is sent.
* (i.e, the `gxp_uci_send_command` function is called)
* It will be poped when the response is consumed by the vd.
*/
struct list_head wait_list_entry;
/*
* List entry which will be inserted to the dest_queue of the vd.
* It will be pushed into the dest_queue when the response is arrived or timed out.
* It will be poped when the response is consumed by the vd.
*/
struct list_head dest_list_entry;
/* Stores the response. */
struct gxp_uci_response resp;
struct gxp_uci *uci;
/* Queue where to be removed from once it is complete or timed out. */
struct list_head *wait_queue;
/* Queue to add the response to once it is complete or timed out. */
struct list_head *dest_queue;
/*
* The lock that protects queues pointed to by `dest_queue` and `wait_queue`.
* The mailbox code also uses this lock to protect changes to the `wait_queue` pointer
* itself when processing this response.
*/
spinlock_t *queue_lock;
/* Queue of clients to notify when this response is processed. */
wait_queue_head_t *dest_queue_waitq;
/* gxp_eventfd to signal when the response completes. May be NULL. */
struct gxp_eventfd *eventfd;
/* The request was sent from this virtual device. */
struct gxp_virtual_device *vd;
/* Handles arrival, timeout of async response. */
struct gcip_mailbox_resp_awaiter *awaiter;
/* Status of the response. */
enum gxp_response_status status;
/* Additional info buffer. */
struct gxp_mapped_resource additional_info_buf;
/* In-fences. */
struct gcip_fence_array *in_fences;
/* Out-fences. */
struct gcip_fence_array *out_fences;
};
struct gxp_uci_wait_list {
struct list_head list;
struct gxp_uci_response *resp;
bool is_async;
};
struct gxp_uci {
struct gxp_dev *gxp;
struct gxp_mcu *mcu;
struct gxp_mailbox *mbx;
struct gxp_mapped_resource cmd_queue_mem;
struct gxp_mapped_resource resp_queue_mem;
struct gxp_mapped_resource descriptor_mem;
};
/* UCI APIs */
/**
* gxp_uci_init() - API for initializing GXP UCI in MCU, should only be
* called while initializing MCU
* @mcu: The MCU that UCI communicate with
*
* Return:
* * 0 - Initialization finished successfully
* * -ENOMEM - Cannot get memory to finish init.
*/
int gxp_uci_init(struct gxp_mcu *mcu);
/**
* gxp_uci_reinit() - Re-initializes the initialized UCI object.
* @uci: The UCI to be initialized
*
* Return:
* * 0 - Initialization finished successfully
*/
int gxp_uci_reinit(struct gxp_uci *uci);
/**
* gxp_uci_exit() - API for releasing the UCI mailbox of MCU.
* @uci: The UCI to be released
*/
void gxp_uci_exit(struct gxp_uci *uci);
/*
* gxp_uci_send_command() - API for sending @cmd to MCU firmware, and
* registering @resp_queue to put the response in after MCU firmware handle the
* command.
*
* Returns 0 on success, a negative errno on failure.
*/
int gxp_uci_send_command(struct gxp_uci *uci, struct gxp_virtual_device *vd,
struct gxp_uci_command *cmd,
struct gxp_uci_additional_info *additional_info,
struct gcip_fence_array *in_fences, struct gcip_fence_array *out_fences,
struct list_head *wait_queue, struct list_head *resp_queue,
spinlock_t *queue_lock, wait_queue_head_t *queue_waitq,
struct gxp_eventfd *eventfd, gcip_mailbox_cmd_flags_t flags);
/**
* gxp_uci_create_and_send_cmd() - Create and put the UCI command into the queue.
* @client: The client which request the UCI command.
* @cmd_seq: The specified sequence number used for this uci command.
* @flags: Same as gxp_mailbox_uci_command_ioctl.
* @opaque: Same as gxp_mailbox_uci_command_ioctl.
* @timeout_ms: Same as gxp_mailbox_uci_command_ioctl.
* @in_fences: Same as gxp_mailbox_uci_command_ioctl.
* @out_fences: Same as gxp_mailbox_uci_command_ioctl.
*
* Following tasks will be done in this function:
* 1. Check the client and its virtual device to see if they are still available.
* 2. Prepare UCI command object.
* 3. Prepare UCI additional info.
* 4. Put the UCI command into the queue.
*
* Return: 0 on success or errno on failure.
*/
int gxp_uci_create_and_send_cmd(struct gxp_client *client, u64 cmd_seq, u32 flags, const u8 *opaque,
u32 timeout_ms, struct gcip_fence_array *in_fences,
struct gcip_fence_array *out_fences);
/*
* gxp_uci_wait_async_response() - API for waiting and fetching a response from
* MCU firmware.
*
* Returns 0 on success, a negative errno on failure.
*/
int gxp_uci_wait_async_response(struct mailbox_resp_queue *uci_resp_queue,
u64 *resp_seq, u16 *error_code, u8 *opaque);
/*
* gxp_uci_fill_additional_info() - Fills @info according to the passed additional information.
* It is expected that the filled @info will be passed to the `gxp_uci_send_command`.
*/
void gxp_uci_fill_additional_info(struct gxp_uci_additional_info *info, uint16_t *in_fences,
uint32_t in_fences_size, uint16_t *out_fences,
uint32_t out_fences_size, uint32_t timeout_ms,
uint8_t *runtime_additional_info,
uint32_t runtime_additional_info_size);
/**
* gxp_uci_cmd_work_create_and_schedule() - Creates a UCI command work and registers it to the given
* DMA fence.
* @fence: The DMA fence to add the callback for the UCI work.
* @client: The attribute of gxp_uci_cmd_work.
* @ibuf: The gxp_mailbox_uci_command_ioctl passed from user.
* @cmd_seq: The attribute of gxp_uci_cmd_work.
* @in_fences: The fences which will signal @fence.
* @out_fences: The fences which will be signaled by @fence.
*
* This function creates a deferred work which will be scheduled when @fence is signaled and will
* call the gxp_uci_create_and_send_cmd function.
*
* If the given fence is NULL or the fence has already been signaled, skips the register process and
* run gxp_uci_create_and_send_cmd() directly.
*
* The work will be added to the @client->uci_cb_list for tracking if the callback is added to the
* fence successfully.
*
* This function should be called in the ioctl function only, where the gxp_client_destroy() is
* guaranteed not to be called simultaneously.
*
* Return: 0 on success or errno on failure.
*/
int gxp_uci_cmd_work_create_and_schedule(struct dma_fence *fence, struct gxp_client *client,
const struct gxp_mailbox_uci_command_ioctl *ibuf,
u64 cmd_seq, struct gcip_fence_array *in_fences,
struct gcip_fence_array *out_fences);
/**
* gxp_uci_work_destroy() - The revert function of gxp_uci_cmd_work_create().
* @uci_work: The target work to be destroyed.
*/
void gxp_uci_work_destroy(struct gxp_uci_cmd_work *uci_work);
#endif /* __GXP_UCI_H__ */