blob: 1088303eb8848e68583557117173256ccabd7e07 [file] [log] [blame]
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* GCIP Mailbox Interface.
*
* Copyright (C) 2022 Google LLC
*/
#ifndef __GCIP_MAILBOX_H__
#define __GCIP_MAILBOX_H__
#include <linux/compiler.h>
#include <linux/mutex.h>
#include <linux/refcount.h>
#include <linux/spinlock.h>
#include <linux/types.h>
#include <linux/wait.h>
#include <linux/workqueue.h>
#define CIRC_QUEUE_WRAPPED(idx, wrap_bit) ((idx)&wrap_bit)
#define CIRC_QUEUE_INDEX_MASK(wrap_bit) (wrap_bit - 1)
#define CIRC_QUEUE_VALID_MASK(wrap_bit) (CIRC_QUEUE_INDEX_MASK(wrap_bit) | wrap_bit)
#define CIRC_QUEUE_REAL_INDEX(idx, wrap_bit) ((idx)&CIRC_QUEUE_INDEX_MASK(wrap_bit))
#define CIRC_QUEUE_MAX_SIZE(wrap_bit) (wrap_bit - 1)
/*
* The status field in a firmware response is set to this by us when the response is fetched from
* the queue.
*/
#define GCIP_MAILBOX_STATUS_OK (0)
/*
* gcip_mailbox#wait_list uses this value to record the status of responses that haven't been
* received yet.
*/
#define GCIP_MAILBOX_STATUS_WAITING_RESPONSE (1)
/*
* Used when an expected response is not received, see the documentation of
* gcip_mailbox_consume_wait_list() for details.
*/
#define GCIP_MAILBOX_STATUS_NO_RESPONSE (2)
/*
* With this flag, the sequence number of the command will not be assigned by set_cmd_elem_seq.
* The sequence number in the mailbox is not increased either.
* The command's sequence number must be pre-set before passing to gcip_mailbox_put_cmd_flags().
*/
#define GCIP_MAILBOX_CMD_FLAGS_SKIP_ASSIGN_SEQ BIT(0)
typedef u32 gcip_mailbox_cmd_flags_t;
/* To specify the operation is toward cmd or resp queue. */
enum gcip_mailbox_queue_type { GCIP_MAILBOX_CMD_QUEUE, GCIP_MAILBOX_RESP_QUEUE };
/* Utilities of circular queue operations */
/*
* Returns the number of elements in a circular queue given its @head, @tail,
* and @queue_size.
*/
static inline u32 gcip_circ_queue_cnt(u32 head, u32 tail, u32 queue_size, u32 wrap_bit)
{
u32 ret;
if (CIRC_QUEUE_WRAPPED(tail, wrap_bit) != CIRC_QUEUE_WRAPPED(head, wrap_bit))
ret = queue_size - CIRC_QUEUE_REAL_INDEX(head, wrap_bit) +
CIRC_QUEUE_REAL_INDEX(tail, wrap_bit);
else
ret = tail - head;
if (unlikely(ret > queue_size))
return 0;
return ret;
}
/* Increases @index of a circular queue by @inc. */
static inline u32 gcip_circ_queue_inc(u32 index, u32 inc, u32 queue_size, u32 wrap_bit)
{
u32 new_index = CIRC_QUEUE_REAL_INDEX(index, wrap_bit) + inc;
if (unlikely(new_index >= queue_size))
return (index + inc - queue_size) ^ wrap_bit;
else
return index + inc;
}
/*
* Checks if @size is a valid circular queue size, which should be a positive
* number and less than or equal to MAX_QUEUE_SIZE.
*/
static inline bool gcip_valid_circ_queue_size(u32 size, u32 wrap_bit)
{
if (!size || size > CIRC_QUEUE_MAX_SIZE(wrap_bit))
return false;
return true;
}
struct gcip_mailbox;
/*
* A struct wraps the IP-defined response to manage additional information such as status needed by
* the logic of GCIP.
*/
struct gcip_mailbox_async_resp {
/* Status code. Must be one of GCIP_MAILBOX_STATUS_*. */
uint16_t status;
/* IP-defined response. */
void *resp;
};
/* Wrapper struct for responses consumed by a thread other than the one which sent the command. */
struct gcip_mailbox_resp_awaiter {
/* Response. */
struct gcip_mailbox_async_resp async_resp;
/* The work which will be executed when the timeout occurs. */
struct delayed_work timeout_work;
/*
* If this response times out, this pointer to the owning mailbox is
* needed to delete this response from the list of pending responses.
*/
struct gcip_mailbox *mailbox;
/* User-defined data. */
void *data;
/* Reference count. */
refcount_t refs;
/*
* The callback for releasing the @data.
* It will be set as @release_awaiter_data of struct gcip_mailbox_ops.
*/
void (*release_data)(void *data);
};
/*
* Mailbox operators.
* For in_interrupt() context, see the implementation of gcip_mailbox_handle_irq for details.
*/
struct gcip_mailbox_ops {
/* Mandatory. */
/*
* Gets the head of mailbox command queue.
* Context: normal.
*/
u32 (*get_cmd_queue_head)(struct gcip_mailbox *mailbox);
/*
* Gets the tail of mailbox command queue.
* Context: normal.
*/
u32 (*get_cmd_queue_tail)(struct gcip_mailbox *mailbox);
/*
* Increases the tail of mailbox command queue by @inc.
* Context: normal.
*/
void (*inc_cmd_queue_tail)(struct gcip_mailbox *mailbox, u32 inc);
/*
* Acquires the lock of cmd_queue. If @try is true, "_trylock" functions can be used, but
* also it can be ignored. If the lock will make the context atomic, @atomic must be set
* to true. Returns 1 if succeed, 0 if failed.
*
* This callback will be called in the following situations.
* - Enqueue a command to the cmd_queue.
*
* The lock can be mutex lock or spin lock and it will be released by calling
* `release_cmd_queue_lock` callback.
*
* Context: normal.
*/
int (*acquire_cmd_queue_lock)(struct gcip_mailbox *mailbox, bool try, bool *atomic);
/*
* Releases the lock of cmd_queue which is acquired by calling `acquire_cmd_queue_lock`.
* Context: normal.
*/
void (*release_cmd_queue_lock)(struct gcip_mailbox *mailbox);
/*
* Gets the sequence number of @cmd queue element.
* Context: normal.
*/
u64 (*get_cmd_elem_seq)(struct gcip_mailbox *mailbox, void *cmd);
/*
* Sets the sequence number of @cmd queue element.
* Context: normal.
*/
void (*set_cmd_elem_seq)(struct gcip_mailbox *mailbox, void *cmd, u64 seq);
/*
* Gets the code of @cmd queue element.
* Context: normal.
*/
u32 (*get_cmd_elem_code)(struct gcip_mailbox *mailbox, void *cmd);
/*
* Gets the size of mailbox response queue.
* Context: normal.
*/
u32 (*get_resp_queue_size)(struct gcip_mailbox *mailbox);
/*
* Gets the head of mailbox response queue.
* Context: normal and in_interrupt().
*/
u32 (*get_resp_queue_head)(struct gcip_mailbox *mailbox);
/*
* Gets the tail of mailbox response queue.
* Context: normal and in_interrupt().
*/
u32 (*get_resp_queue_tail)(struct gcip_mailbox *mailbox);
/*
* Increases the head of mailbox response queue by @inc.
* Context: normal and in_interrupt().
*/
void (*inc_resp_queue_head)(struct gcip_mailbox *mailbox, u32 inc);
/*
* Acquires the lock of resp_queue. If @try is true, "_trylock" functions can be used, but
* also it can be ignored. If the lock will make the context atomic, @atomic must be set
* to true. Returns 1 if succeed, 0 if failed.
*
* This callback will be called in the following situations:
* - Fetch response(s) from the resp_queue.
*
* The lock can be a mutex lock or a spin lock. However, if @try is considered and the
* "_trylock" is used, it must be a spin lock only.
*
* The lock will be released by calling `release_resp_queue_lock` callback.
*
* Context: normal and in_interrupt().
*/
int (*acquire_resp_queue_lock)(struct gcip_mailbox *mailbox, bool try, bool *atomic);
/*
* Releases the lock of resp_queue which is acquired by calling `acquire_resp_queue_lock`.
* Context: normal and in_interrupt().
*/
void (*release_resp_queue_lock)(struct gcip_mailbox *mailbox);
/*
* Gets the sequence number of @resp queue element.
* Context: normal and in_interrupt().
*/
u64 (*get_resp_elem_seq)(struct gcip_mailbox *mailbox, void *resp);
/*
* Sets the sequence number of @resp queue element.
* Context: normal and in_interrupt().
*/
void (*set_resp_elem_seq)(struct gcip_mailbox *mailbox, void *resp, u64 seq);
/*
* Acquires the lock of wait_list. If @irqsave is true, "_irqsave" functions can be used to
* store the irq state to @flags, but also it can be ignored.
* This callback will be called in following situations.
* - Push a waiting response to the @mailbox->wait_list.
* - Delete a waiting response from the @mailbox->wait_list.
* - Handle an arrived response and delete it from the @mailbox->wait_list.
* - Flush the asynchronous responses in the @mailbox->wait_list when release the @mailbox.
* The lock can be a mutex lock or a spin lock. However, if @irqsave is considered and
* "_irqsave" is used, it must be spin lock only.
* The lock will be released by calling `release_wait_list_lock` callback.
* Context: normal and in_interrupt().
*/
void (*acquire_wait_list_lock)(struct gcip_mailbox *mailbox, bool irqsave,
unsigned long *flags);
/*
* Releases the lock of wait_list which is acquired by calling `acquire_wait_list_lock`.
* If @irqsave is true, restores @flags from `acquire_wait_list_lock` to the irq state.
* Or it can be ignored, if @irqsave was not considered in the `acquire_wait_list_lock`.
* Context: normal and in_interrupt().
*/
void (*release_wait_list_lock)(struct gcip_mailbox *mailbox, bool irqrestore,
unsigned long flags);
/* Optional. */
/*
* Waits for the cmd queue of @mailbox has a available space for putting the command. If
* the queue has a space, returns 0. Otherwise, returns error as non-zero value. It depends
* on the implementation details, but it is okay to return right away with error when the
* queue is full. If this callback returns an error, `gcip_mailbox_send_cmd` function or
* `gcip_mailbox_put_cmd` function will return that error too. This callback is called with
* the `cmd_queue_lock` being held.
*
* Note: if this callback is NULL, it will simply check the fullness of cmd_queue and
* return -EAGAIN error right away if it is full. Please refer the implementation of the
* `gcip_mailbox_enqueue_cmd` function.
*
* Context: normal.
*/
int (*wait_for_cmd_queue_not_full)(struct gcip_mailbox *mailbox);
/*
* This callback will be called before putting the @resp into @mailbox->wait_list and
* putting @cmd of @resp into the command queue. After this callback returns, the consumer
* is able to start processing it and the mailbox is going to wait for it. Therefore, this
* callback is the final checkpoint of deciding whether it is good to wait for the response
* or not. If you don't want to wait for it, return a non-zero value error.
*
* If the implement side has its own wait queue, this callback is suitable to put @resp or
* @awaiter into that.
*
* If @resp is synchronous, @awaiter will be NULL.
*
* Context: normal.
*/
int (*before_enqueue_wait_list)(struct gcip_mailbox *mailbox, void *resp,
struct gcip_mailbox_resp_awaiter *awaiter);
/*
* This callback will be called after putting the @cmd to the command queue. It can be used
* for triggering the doorbell. Returns 0 on success, or returns error code otherwise.
* This is called with the `cmd_queue_lock` being held.
* Context: normal.
*/
int (*after_enqueue_cmd)(struct gcip_mailbox *mailbox, void *cmd);
/*
* This callback will be called after fetching responses. It can be used for triggering
* a signal to break up waiting consuming the response queue. This is called without
* holding any locks.
* - @num_resps: the number of fetched responses.
* Context: normal and in_interrupt().
*/
void (*after_fetch_resps)(struct gcip_mailbox *mailbox, u32 num_resps);
/*
* Before handling each fetched responses, this callback will be called. If this callback
* is not defined or returns true, the mailbox will handle the @resp normally. If the @resp
* should not be handled, returns false. This is called without holding any locks.
* Context: normal and in_interrupt().
*/
bool (*before_handle_resp)(struct gcip_mailbox *mailbox, const void *resp);
/*
* Handles the asynchronous response which arrives well. How to handle it depends on the
* chip implementation. However, @awaiter should be released by calling the
* `gcip_mailbox_release_awaiter` function when the kernel driver doesn't need
* @awaiter anymore.
* Context: normal and in_interrupt().
*/
void (*handle_awaiter_arrived)(struct gcip_mailbox *mailbox,
struct gcip_mailbox_resp_awaiter *awaiter);
/*
* Handles the timed out asynchronous response. How to handle it depends on the chip
* implementation. However, @awaiter should be released by calling the
* `gcip_mailbox_release_awaiter` function when the kernel driver doesn't need
* @awaiter anymore. This is called without holding any locks.
* Context: normal and in_interrupt().
*/
void (*handle_awaiter_timedout)(struct gcip_mailbox *mailbox,
struct gcip_mailbox_resp_awaiter *awaiter);
/*
* Cleans up asynchronous response which is not arrived yet, but also not timed out.
* The @awaiter should be marked as unprocessable to make it not to be processed by
* the `handle_awaiter_arrived` or `handle_awaiter_timedout` callbacks in race
* conditions. Don't have to release @awaiter of this function by calling the
* `gcip_mailbox_release_awaiter` function. It will be released internally. This is
* called with the `wait_list_lock` being held.
* Context: normal.
*/
void (*flush_awaiter)(struct gcip_mailbox *mailbox,
struct gcip_mailbox_resp_awaiter *awaiter);
/*
* Releases the @data which was passed to the `gcip_mailbox_put_cmd` function. This is
* called without holding any locks.
* Context: normal and in_interrupt().
*/
void (*release_awaiter_data)(void *data);
/*
* Checks if the block is off.
* Context: in_interrupt()
*/
bool (*is_block_off)(struct gcip_mailbox *mailbox);
/*
* Retrieves the per command timeout value in milliseconds set by the user for the given
* mailbox command @cmd. According to the implementation detail of IP side, the timeout can
* be fetched from @cmd, @resp or @data passed to the `gcip_mailbox_put_cmd` function.
* Therefore, this callback passes all of them not only @cmd. This can be called without
* holding any locks.
* Context: normal.
*/
u32 (*get_cmd_timeout)(struct gcip_mailbox *mailbox, void *cmd, void *resp, void *data);
/*
* Called when a command fails to be sent.
* Context: normal.
*/
void (*on_error)(struct gcip_mailbox *mailbox, int err);
};
struct gcip_mailbox {
/* Device used for logging and memory allocation. */
struct device *dev;
/* Warp bit for both cmd and resp queue. */
u64 queue_wrap_bit;
/* Cmd sequence number. */
u64 cur_seq;
/* Cmd queue pointer. */
void *cmd_queue;
/* Size of element of cmd queue. */
u32 cmd_elem_size;
/* Resp queue pointer. */
void *resp_queue;
/* Size of element of resp queue. */
u32 resp_elem_size;
/* List of commands that need to wait for responses. */
struct list_head wait_list;
/* Queue for waiting for the wait_list to be consumed. */
wait_queue_head_t wait_list_waitq;
/* Mailbox timeout in milliseconds. */
u32 timeout;
/* Mailbox operators. */
const struct gcip_mailbox_ops *ops;
/* User-defined data. */
void *data;
};
/* Arguments for gcip_mailbox_init. See struct gcip_mailbox for details. */
struct gcip_mailbox_args {
struct device *dev;
u32 queue_wrap_bit;
void *cmd_queue;
u32 cmd_elem_size;
void *resp_queue;
u32 resp_elem_size;
u32 timeout;
const struct gcip_mailbox_ops *ops;
void *data;
};
/* Initializes a mailbox object. */
int gcip_mailbox_init(struct gcip_mailbox *mailbox, const struct gcip_mailbox_args *args);
/* Releases a mailbox object which is initialized by gcip_mailbox_init */
void gcip_mailbox_release(struct gcip_mailbox *mailbox);
/*
* Fetches and handles responses, then wakes up threads that are waiting for a response.
* To consume response queue and get responses, this function should be used as deferred work
* such as `struct work_struct` or `struct kthread_work`.
*
* Note: this worker is scheduled in the IRQ handler, to prevent use-after-free or race-condition
* bugs, cancel all works before free the mailbox.
*/
void gcip_mailbox_consume_responses_work(struct gcip_mailbox *mailbox);
/*
* Pushes an element to cmd queue and waits for the response (synchronous).
* Returns -ETIMEDOUT if no response is received within mailbox->timeout msecs.
*
* Returns the code of response, or a negative errno on error.
* @resp is updated with the response, as to retrieve returned retval field.
*/
int gcip_mailbox_send_cmd(struct gcip_mailbox *mailbox, void *cmd, void *resp,
gcip_mailbox_cmd_flags_t flags);
/*
* Executes @cmd command asynchronously. This function returns an instance of
* `struct gcip_mailbox_resp_awaiter` which handles the arrival and time-out of the response.
* The implementation side can cancel the asynchronous response by calling the
* `gcip_mailbox_cancel_awaiter` or `gcip_mailbox_cancel_awaiter_timeout` function with it.
*
* Arrived asynchronous response will be handled by `handle_awaiter_arrived` callback and timed out
* asynchronous response will be handled by `handle_awaiter_timedout` callback. Those callbacks
* will pass the @awaiter as a parameter which is the same with the return of this function.
* The response can be accessed from `resp` member of it. Also, the @data passed to this function
* can be accessed from `data` member variable of it. The @awaiter must be released by calling
* the `gcip_mailbox_release_awaiter` function when it is not needed anymore.
*
* If the mailbox is released before the response arrives, all the waiting asynchronous responses
* will be flushed. In this case, the `flush_awaiter` callback will be called for that response
* and @awaiter don't have to be released by the implementation side.
* (i.e, the `gcip_mailbox_release_awaiter` function will be called internally.)
*
* The caller defines the way of cleaning up the @data to the `release_awaiter_data` callback.
* This callback will be called when the `gcip_mailbox_release_awaiter` function is called or
* the response is flushed.
*
* If this function fails to request the command, it will return the error pointer. In this case,
* the caller should free @data explicitly. (i.e, the callback `release_awaiter_data` will not
* be.)
*
* Note: the asynchronous responses fetched from @resp_queue should be released by calling the
* `gcip_mailbox_release_awaiter` function.
*
* Note: if the life cycle of the mailbox is longer than the caller part, you should make sure
* that the callbacks don't access the variables of caller part after the release of it.
*
* Note: if you don't need the result of the response (e.g., if you pass @resp as NULL), you
* can release the returned awaiter right away by calling the `gcip_mailbox_release_awaiter`
* function.
*/
struct gcip_mailbox_resp_awaiter *gcip_mailbox_put_cmd_flags(struct gcip_mailbox *mailbox,
void *cmd, void *resp, void *data,
gcip_mailbox_cmd_flags_t flags);
/* Calls gcip_mailbox_put_cmd_flags() with flags = 0. */
struct gcip_mailbox_resp_awaiter *gcip_mailbox_put_cmd(struct gcip_mailbox *mailbox, void *cmd,
void *resp, void *data);
/*
* Cancels awaiting the asynchronous response.
* This function will remove @awaiter from the waiting list to make it not to be handled by the
* arrived callback. Also, it will cancel the timeout work of @awaiter synchronously. Therefore,
* AFTER the return of this function, you can guarantee that arrived or timedout callback will
* not be called for @awaiter.
*
* However, by the race condition, you must note that arrived or timedout callback can be executed
* BEFORE this function returns. (i.e, this function and arrived/timedout callback is called at the
* same time but the callback acquired the lock earlier.)
*
* Note: this function will cancel or wait for the completion of arrived or timedout callbacks
* synchronously. Therefore, make sure that the caller side doesn't hold any locks which can be
* acquired by the arrived or timedout callbacks.
*
* If you already got a response of @awaiter and want to ensure that timedout handler is finished,
* you can use the `gcip_mailbox_cancel_awaiter_timeout` function instead.
*/
void gcip_mailbox_cancel_awaiter(struct gcip_mailbox_resp_awaiter *awaiter);
/*
* Cancels the timeout work of the asynchronous response. In normally, the response arrives and
* the timeout is canceled, or the response timed out and the timeout handler executes. However,
* rarely, the response handler cancels the timeout handler while it has been already in progress.
* To handle this and ensure any in-process timeout handler has been able to exit cleanly, it is
* recommended to call this function after fetching the asynchronous response even though the
* response arrived successfully.
*
* Note: this function will cancel or wait for the completion of timedout callbacks synchronously.
* Therefore, make sure that the caller side doesn't hold any locks which can be acquired by the
* timedout callbacks.
*
* If you haven't gotten a response of @awaiter yet and want to make it not to be processed by
* arrived and timedout callbacks, use the `gcip_mailbox_cancel_awaiter` function.
*/
void gcip_mailbox_cancel_awaiter_timeout(struct gcip_mailbox_resp_awaiter *awaiter);
/*
* Releases @awaiter. Every fetched (arrived or timed out) asynchronous responses should be
* released by calling this. It will call the `release_awaiter_data` callback internally.
*/
void gcip_mailbox_release_awaiter(struct gcip_mailbox_resp_awaiter *awaiter);
/*
* Consume one response and handle it. This can be used for consuming one response quickly and then
* schedule `gcip_mailbox_consume_responses_work` work in the IRQ handler of mailbox.
*/
void gcip_mailbox_consume_one_response(struct gcip_mailbox *mailbox, void *resp);
/**
* gcip_mailbox_inc_seq_num() - Increases the sequence number of the mailbox and returns the
* original one.
* @mailbox: The mailbox to increase the sequence number.
* @n: The number that the sequence number needs to be increased.
*
* Return: The sequence number before increasing.
*/
uint gcip_mailbox_inc_seq_num(struct gcip_mailbox *mailbox, uint n);
/* Getters for member variables of the `struct gcip_mailbox`. */
static inline u64 gcip_mailbox_get_cur_seq(struct gcip_mailbox *mailbox)
{
return mailbox->cur_seq;
}
static inline void *gcip_mailbox_get_cmd_queue(struct gcip_mailbox *mailbox)
{
return mailbox->cmd_queue;
}
static inline u32 gcip_mailbox_get_cmd_elem_size(struct gcip_mailbox *mailbox)
{
return mailbox->cmd_elem_size;
}
static inline void *gcip_mailbox_get_resp_queue(struct gcip_mailbox *mailbox)
{
return mailbox->resp_queue;
}
static inline u32 gcip_mailbox_get_resp_elem_size(struct gcip_mailbox *mailbox)
{
return mailbox->resp_elem_size;
}
static inline u64 gcip_mailbox_get_queue_wrap_bit(struct gcip_mailbox *mailbox)
{
return mailbox->queue_wrap_bit;
}
static inline struct list_head *gcip_mailbox_get_wait_list(struct gcip_mailbox *mailbox)
{
return &mailbox->wait_list;
}
static inline u32 gcip_mailbox_get_timeout(struct gcip_mailbox *mailbox)
{
return mailbox->timeout;
}
static inline void *gcip_mailbox_get_data(struct gcip_mailbox *mailbox)
{
return mailbox->data;
}
#endif /* __GCIP_MAILBOX_H__ */