// SPDX-License-Identifier: GPL-2.0-only /* * Legacy implementation of the GXP mailbox interface. * This file must be used only when the kernel driver has to compile the implementation of the * mailbox by itself (i.e, when the target chip can't be compiled with GCIP). * * Copyright (C) 2022 Google LLC */ #include #include "gxp-dma.h" #include "gxp-mailbox-driver.h" #include "gxp-mailbox-impl.h" #include "gxp-mailbox.h" #include "gxp.h" #define CIRCULAR_QUEUE_WRAP_BIT BIT(15) #define MBOX_CMD_QUEUE_NUM_ENTRIES 1024 #define MBOX_RESP_QUEUE_NUM_ENTRIES 1024 static int gxp_mailbox_ops_allocate_resources(struct gxp_mailbox *mailbox, struct gxp_virtual_device *vd, uint virt_core) { int ret; /* Allocate and initialize the command queue */ ret = gxp_dma_alloc_coherent_buf( mailbox->gxp, vd->domain, sizeof(struct gxp_command) * MBOX_CMD_QUEUE_NUM_ENTRIES, GFP_KERNEL, 0, &mailbox->cmd_queue_buf); if (ret) goto err_cmd_queue; mailbox->cmd_queue_size = MBOX_CMD_QUEUE_NUM_ENTRIES; mailbox->cmd_queue_tail = 0; /* Allocate and initialize the response queue */ ret = gxp_dma_alloc_coherent_buf( mailbox->gxp, vd->domain, sizeof(struct gxp_response) * MBOX_RESP_QUEUE_NUM_ENTRIES, GFP_KERNEL, 0, &mailbox->resp_queue_buf); if (ret) goto err_resp_queue; mailbox->resp_queue_size = MBOX_RESP_QUEUE_NUM_ENTRIES; mailbox->resp_queue_head = 0; /* Allocate and initialize the mailbox descriptor */ ret = gxp_dma_alloc_coherent_buf(mailbox->gxp, vd->domain, sizeof(struct gxp_mailbox_descriptor), GFP_KERNEL, 0, &mailbox->descriptor_buf); if (ret) goto err_descriptor; mailbox->descriptor = (struct gxp_mailbox_descriptor *)mailbox->descriptor_buf.vaddr; mailbox->descriptor->cmd_queue_device_addr = mailbox->cmd_queue_buf.dsp_addr; mailbox->descriptor->resp_queue_device_addr = mailbox->resp_queue_buf.dsp_addr; mailbox->descriptor->cmd_queue_size = mailbox->cmd_queue_size; mailbox->descriptor->resp_queue_size = mailbox->resp_queue_size; return 0; err_descriptor: gxp_dma_free_coherent_buf(mailbox->gxp, vd->domain, &mailbox->resp_queue_buf); err_resp_queue: gxp_dma_free_coherent_buf(mailbox->gxp, vd->domain, &mailbox->cmd_queue_buf); err_cmd_queue: return ret; } static void gxp_mailbox_ops_release_resources(struct gxp_mailbox *mailbox, struct gxp_virtual_device *vd, uint virt_core) { gxp_dma_free_coherent_buf(mailbox->gxp, vd->domain, &mailbox->cmd_queue_buf); gxp_dma_free_coherent_buf(mailbox->gxp, vd->domain, &mailbox->resp_queue_buf); gxp_dma_free_coherent_buf(mailbox->gxp, vd->domain, &mailbox->descriptor_buf); } /* * Pops the wait_list until the sequence number of @resp is found, and copies * @resp to the found entry. * * Entries in wait_list should have sequence number in increasing order, but * the responses arriving and being handled may be out-of-order. * * Iterate over the wait_list, comparing #cur->resp->seq with @resp->seq: * 1. #cur->resp->seq > @resp->seq: * - Nothing to do, either @resp is invalid or its command timed out. * - We're done. * 2. #cur->resp->seq == @resp->seq: * - Copy @resp, pop the head. * - If #cur->resp has a destination queue, push it to that queue * - We're done. * 3. #cur->resp->seq < @resp->seq: * - @resp has arrived out of sequence order. * - Leave #cur->resp in the wait_list. * - Keep iterating unless the list is exhausted. */ static void gxp_mailbox_handle_response(struct gxp_mailbox *mailbox, const struct gxp_response *resp) { struct gxp_mailbox_wait_list *cur, *nxt; struct gxp_async_response *async_resp; unsigned long flags; mutex_lock(&mailbox->wait_list_lock); list_for_each_entry_safe (cur, nxt, &mailbox->wait_list, list) { if (cur->resp->seq > resp->seq) { /* * This response has already timed out and been removed * from the wait list (or this is an invalid response). * Drop it. */ break; } if (cur->resp->seq == resp->seq) { memcpy(cur->resp, resp, sizeof(*resp)); list_del(&cur->list); if (cur->is_async) { async_resp = container_of(cur->resp, struct gxp_async_response, resp); cancel_delayed_work(&async_resp->timeout_work); gxp_pm_update_requested_power_states( async_resp->mailbox->gxp, async_resp->requested_states, off_states); spin_lock_irqsave(async_resp->dest_queue_lock, flags); list_add_tail(&async_resp->list_entry, async_resp->dest_queue); /* * Marking the dest_queue as NULL indicates the * response was handled in case its timeout * handler fired between acquiring the * wait_list_lock and cancelling the timeout. */ async_resp->dest_queue = NULL; /* * Don't release the dest_queue_lock until both * any eventfd has been signaled and any waiting * thread has been woken. Otherwise one thread * might consume and free the response before * this function is done with it. */ if (async_resp->eventfd) { gxp_eventfd_signal(async_resp->eventfd); gxp_eventfd_put(async_resp->eventfd); } wake_up(async_resp->dest_queue_waitq); spin_unlock_irqrestore( async_resp->dest_queue_lock, flags); } kfree(cur); break; } } mutex_unlock(&mailbox->wait_list_lock); } /* * Fetches elements in the response queue. * * Returns the pointer of fetched response elements. * @total_ptr will be the number of elements fetched. * * Returns -ENOMEM if failed on memory allocation. * Returns NULL if the response queue is empty. */ static struct gxp_response * gxp_mailbox_fetch_responses(struct gxp_mailbox *mailbox, u32 *total_ptr) { u32 head; u32 tail; u32 count; u32 i; u32 j; u32 total = 0; const u32 size = mailbox->resp_queue_size; const struct gxp_response *queue = mailbox->resp_queue_buf.vaddr; struct gxp_response *ret = NULL; struct gxp_response *prev_ptr = NULL; mutex_lock(&mailbox->resp_queue_lock); head = mailbox->resp_queue_head; /* loop until our head equals to CSR tail */ while (1) { tail = gxp_mailbox_read_resp_queue_tail(mailbox); count = gxp_circ_queue_cnt(head, tail, size, CIRCULAR_QUEUE_WRAP_BIT); if (count == 0) break; prev_ptr = ret; ret = krealloc(prev_ptr, (total + count) * sizeof(*queue), GFP_KERNEL); /* * Out-of-memory, we can return the previously fetched responses * if any, or ENOMEM otherwise. */ if (!ret) { if (!prev_ptr) ret = ERR_PTR(-ENOMEM); else ret = prev_ptr; break; } /* copy responses */ j = CIRCULAR_QUEUE_REAL_INDEX(head, CIRCULAR_QUEUE_WRAP_BIT); for (i = 0; i < count; i++) { memcpy(&ret[total], &queue[j], sizeof(*queue)); ret[total].status = GXP_RESP_OK; j = (j + 1) % size; total++; } head = gxp_circ_queue_inc(head, count, size, CIRCULAR_QUEUE_WRAP_BIT); } gxp_mailbox_inc_resp_queue_head_locked(mailbox, total, CIRCULAR_QUEUE_WRAP_BIT); mutex_unlock(&mailbox->resp_queue_lock); /* * Now that the response queue has been drained, send an interrupt * to the device in case firmware was waiting for us to consume * responses. */ if (total == size) { /* TODO(b/190868834) define interrupt bits */ gxp_mailbox_generate_device_interrupt(mailbox, BIT(0)); } *total_ptr = total; return ret; } /* Default operators for the DSP mailbox */ struct gxp_mailbox_ops gxp_mailbox_default_ops = { .allocate_resources = gxp_mailbox_ops_allocate_resources, .release_resources = gxp_mailbox_ops_release_resources, }; /* Default arguments for the DSP mailbox */ const struct gxp_mailbox_args gxp_mailbox_default_args = { .type = GXP_MBOX_TYPE_GENERAL, .ops = &gxp_mailbox_default_ops, .data = NULL, }; /* * Adds @resp to @mailbox->wait_list. * * wait_list is a FIFO queue, with sequence number in increasing order. * * Returns 0 on success, or -ENOMEM if failed on allocation. */ static int gxp_mailbox_push_wait_resp(struct gxp_mailbox *mailbox, struct gxp_response *resp, bool is_async) { struct gxp_mailbox_wait_list *entry = kzalloc(sizeof(*entry), GFP_KERNEL); if (!entry) return -ENOMEM; entry->resp = resp; entry->is_async = is_async; mutex_lock(&mailbox->wait_list_lock); list_add_tail(&entry->list, &mailbox->wait_list); mutex_unlock(&mailbox->wait_list_lock); return 0; } /* * Removes the response previously pushed with gxp_mailbox_push_wait_resp(). * * This is used when the kernel gives up waiting for the response. */ static void gxp_mailbox_del_wait_resp(struct gxp_mailbox *mailbox, struct gxp_response *resp) { struct gxp_mailbox_wait_list *cur; mutex_lock(&mailbox->wait_list_lock); list_for_each_entry (cur, &mailbox->wait_list, list) { if (cur->resp->seq > resp->seq) { /* * Sequence numbers in wait_list are in increasing * order. This case implies no entry in the list * matches @resp's sequence number. */ break; } if (cur->resp->seq == resp->seq) { list_del(&cur->list); kfree(cur); break; } } mutex_unlock(&mailbox->wait_list_lock); } static int gxp_mailbox_enqueue_cmd(struct gxp_mailbox *mailbox, struct gxp_command *cmd, struct gxp_response *resp, bool resp_is_async) { int ret; u32 tail; struct gxp_command *cmd_queue = mailbox->cmd_queue_buf.vaddr; mutex_lock(&mailbox->cmd_queue_lock); cmd->seq = mailbox->cur_seq; /* * The lock ensures mailbox->cmd_queue_tail cannot be changed by * other processes (this method should be the only one to modify the * value of tail), therefore we can remember its value here and use it * in various places below. */ tail = mailbox->cmd_queue_tail; /* * If the cmd queue is full, it's up to the caller to retry. */ if (gxp_mailbox_read_cmd_queue_head(mailbox) == (tail ^ CIRCULAR_QUEUE_WRAP_BIT)) { ret = -EAGAIN; goto out; } if (resp) { /* * Add @resp to the wait_list only if the cmd can be pushed * successfully. */ resp->seq = cmd->seq; resp->status = GXP_RESP_WAITING; ret = gxp_mailbox_push_wait_resp(mailbox, resp, resp_is_async); if (ret) goto out; } /* size of cmd_queue is a multiple of sizeof(*cmd) */ memcpy(cmd_queue + CIRCULAR_QUEUE_REAL_INDEX(tail, CIRCULAR_QUEUE_WRAP_BIT), cmd, sizeof(*cmd)); gxp_mailbox_inc_cmd_queue_tail_locked(mailbox, 1, CIRCULAR_QUEUE_WRAP_BIT); /* triggers doorbell */ /* TODO(b/190868834) define interrupt bits */ gxp_mailbox_generate_device_interrupt(mailbox, BIT(0)); /* bumps sequence number after the command is sent */ mailbox->cur_seq++; ret = 0; out: mutex_unlock(&mailbox->cmd_queue_lock); if (ret) dev_err(mailbox->gxp->dev, "%s: ret=%d", __func__, ret); return ret; } static int gxp_mailbox_execute_cmd(struct gxp_mailbox *mailbox, struct gxp_command *cmd, struct gxp_response *resp) { int ret; ret = gxp_mailbox_enqueue_cmd(mailbox, cmd, resp, /* resp_is_async = */ false); if (ret) return ret; ret = wait_event_timeout(mailbox->wait_list_waitq, resp->status != GXP_RESP_WAITING, msecs_to_jiffies(MAILBOX_TIMEOUT)); if (!ret) { dev_notice(mailbox->gxp->dev, "%s: event wait timeout", __func__); gxp_mailbox_del_wait_resp(mailbox, resp); return -ETIMEDOUT; } if (resp->status != GXP_RESP_OK) { dev_notice(mailbox->gxp->dev, "%s: resp status=%u", __func__, resp->status); return -ENOMSG; } return resp->retval; } static void async_cmd_timeout_work(struct work_struct *work) { struct gxp_async_response *async_resp = container_of( work, struct gxp_async_response, timeout_work.work); unsigned long flags; /* * This function will acquire the mailbox wait_list_lock. This means if * response processing is in progress, it will complete before this * response can be removed from the wait list. * * Once this function has the wait_list_lock, no future response * processing will begin until this response has been removed. */ gxp_mailbox_del_wait_resp(async_resp->mailbox, &async_resp->resp); /* * Check if this response still has a valid destination queue, in case * an in-progress call to `gxp_mailbox_handle_response()` completed * the response while `gxp_mailbox_del_wait_resp()` was waiting for * the wait_list_lock. */ spin_lock_irqsave(async_resp->dest_queue_lock, flags); if (async_resp->dest_queue) { async_resp->resp.status = GXP_RESP_CANCELLED; list_add_tail(&async_resp->list_entry, async_resp->dest_queue); spin_unlock_irqrestore(async_resp->dest_queue_lock, flags); gxp_pm_update_requested_power_states( async_resp->mailbox->gxp, async_resp->requested_states, off_states); if (async_resp->eventfd) { gxp_eventfd_signal(async_resp->eventfd); gxp_eventfd_put(async_resp->eventfd); } wake_up(async_resp->dest_queue_waitq); } else { spin_unlock_irqrestore(async_resp->dest_queue_lock, flags); } } static int gxp_mailbox_execute_cmd_async(struct gxp_mailbox *mailbox, struct gxp_command *cmd, struct list_head *resp_queue, spinlock_t *queue_lock, wait_queue_head_t *queue_waitq, struct gxp_power_states power_states, struct gxp_eventfd *eventfd) { struct gxp_async_response *async_resp; int ret; async_resp = kzalloc(sizeof(*async_resp), GFP_KERNEL); if (!async_resp) return -ENOMEM; async_resp->mailbox = mailbox; async_resp->dest_queue = resp_queue; async_resp->dest_queue_lock = queue_lock; async_resp->dest_queue_waitq = queue_waitq; async_resp->requested_states = power_states; if (eventfd && gxp_eventfd_get(eventfd)) async_resp->eventfd = eventfd; else async_resp->eventfd = NULL; INIT_DELAYED_WORK(&async_resp->timeout_work, async_cmd_timeout_work); schedule_delayed_work(&async_resp->timeout_work, msecs_to_jiffies(MAILBOX_TIMEOUT)); gxp_pm_update_requested_power_states(mailbox->gxp, off_states, power_states); ret = gxp_mailbox_enqueue_cmd(mailbox, cmd, &async_resp->resp, /* resp_is_async = */ true); if (ret) goto err_free_resp; return 0; err_free_resp: gxp_pm_update_requested_power_states(mailbox->gxp, power_states, off_states); cancel_delayed_work_sync(&async_resp->timeout_work); kfree(async_resp); return ret; } static struct gxp_mailbox * gxp_mailbox_manager_allocate_mailbox(struct gxp_mailbox_manager *mgr, struct gxp_virtual_device *vd, uint virt_core, u8 core_id) { struct gxp_mailbox *mailbox = gxp_mailbox_alloc( mgr, vd, virt_core, core_id, &gxp_mailbox_default_args); if (!IS_ERR(mailbox)) gxp_mailbox_generate_device_interrupt(mailbox, BIT(0)); return mailbox; } static int gxp_mailbox_manager_execute_cmd( struct gxp_client *client, struct gxp_mailbox *mailbox, int virt_core, u16 cmd_code, u8 cmd_priority, u64 cmd_daddr, u32 cmd_size, u32 cmd_flags, u8 num_cores, struct gxp_power_states power_states, u64 *resp_seq, u16 *resp_status) { struct gxp_dev *gxp = client->gxp; struct gxp_command cmd; struct gxp_response resp; struct buffer_descriptor buffer; int ret; /* Pack the command structure */ buffer.address = cmd_daddr; buffer.size = cmd_size; buffer.flags = cmd_flags; /* cmd.seq is assigned by mailbox implementation */ cmd.code = cmd_code; /* All IOCTL commands are dispatch */ cmd.priority = cmd_priority; /* currently unused */ cmd.buffer_descriptor = buffer; down_read(&gxp->vd_semaphore); ret = gxp_mailbox_execute_cmd(mailbox, &cmd, &resp); up_read(&gxp->vd_semaphore); /* resp.seq and resp.status can be updated even though it failed to process the command */ if (resp_seq) *resp_seq = resp.seq; if (resp_status) *resp_status = resp.status; return ret; } static int gxp_mailbox_manager_execute_cmd_async( struct gxp_client *client, struct gxp_mailbox *mailbox, int virt_core, u16 cmd_code, u8 cmd_priority, u64 cmd_daddr, u32 cmd_size, u32 cmd_flags, struct gxp_power_states power_states, u64 *cmd_seq) { struct gxp_command cmd; struct buffer_descriptor buffer; struct mailbox_resp_queue *resp_queue = &client->vd->mailbox_resp_queues[virt_core]; struct gxp_eventfd *eventfd = client->mb_eventfds[virt_core]; int ret; /* Pack the command structure */ buffer.address = cmd_daddr; buffer.size = cmd_size; buffer.flags = cmd_flags; /* cmd.seq is assigned by mailbox implementation */ cmd.code = cmd_code; /* All IOCTL commands are dispatch */ cmd.priority = cmd_priority; /* currently unused */ cmd.buffer_descriptor = buffer; ret = gxp_mailbox_execute_cmd_async( mailbox, &cmd, &resp_queue->dest_queue, &resp_queue->lock, &resp_queue->waitq, power_states, eventfd); if (cmd_seq) *cmd_seq = cmd.seq; return ret; } static int gxp_mailbox_manager_wait_async_resp(struct gxp_client *client, int virt_core, u64 *resp_seq, u16 *resp_status, u32 *resp_retval, u16 *error_code) { struct gxp_async_response *resp_ptr; struct mailbox_resp_queue *resp_queue = &client->vd->mailbox_resp_queues[virt_core]; long timeout; spin_lock_irq(&resp_queue->lock); /* * The "exclusive" version of wait_event is used since each wake * corresponds to the addition of exactly one new response to be * consumed. Therefore, only one waiting response ioctl can ever * proceed per wake event. */ timeout = wait_event_interruptible_lock_irq_timeout_exclusive( resp_queue->waitq, !list_empty(&resp_queue->dest_queue), resp_queue->lock, msecs_to_jiffies(MAILBOX_TIMEOUT)); if (timeout <= 0) { spin_unlock_irq(&resp_queue->lock); /* unusual case - this only happens when there is no command pushed */ return timeout ? -ETIMEDOUT : timeout; } resp_ptr = list_first_entry(&resp_queue->dest_queue, struct gxp_async_response, list_entry); /* Pop the front of the response list */ list_del(&(resp_ptr->list_entry)); spin_unlock_irq(&resp_queue->lock); if (resp_seq) *resp_seq = resp_ptr->resp.seq; if (resp_status) *resp_status = resp_ptr->resp.status; switch (resp_ptr->resp.status) { case GXP_RESP_OK: if (error_code) *error_code = GXP_RESPONSE_ERROR_NONE; /* retval is only valid if status == GXP_RESP_OK */ if (resp_retval) *resp_retval = resp_ptr->resp.retval; break; case GXP_RESP_CANCELLED: if (error_code) *error_code = GXP_RESPONSE_ERROR_TIMEOUT; break; default: /* No other status values are valid at this point */ WARN(true, "Completed response had invalid status %hu", resp_ptr->resp.status); if (error_code) *error_code = GXP_RESPONSE_ERROR_INTERNAL; break; } /* * We must be absolutely sure the timeout work has been cancelled * and/or completed before freeing the `gxp_async_response`. * There are 3 possible cases when we arrive at this point: * 1) The response arrived normally and the timeout was cancelled * 2) The response timedout and its timeout handler finished * 3) The response handler and timeout handler raced, and the response * handler "cancelled" the timeout handler while it was already in * progress. * * This call handles case #3, and ensures any in-process timeout * handler (which may reference the `gxp_async_response`) has * been able to exit cleanly. */ cancel_delayed_work_sync(&resp_ptr->timeout_work); kfree(resp_ptr); return 0; } static void gxp_mailbox_manager_release_unconsumed_async_resps( struct gxp_virtual_device *vd) { struct gxp_async_response *cur, *nxt; int i; unsigned long flags; /* Cleanup any unconsumed responses */ for (i = 0; i < vd->num_cores; i++) { /* * Since VD is releasing, it is not necessary to lock here. * Do it anyway for consistency. */ spin_lock_irqsave(&vd->mailbox_resp_queues[i].lock, flags); list_for_each_entry_safe ( cur, nxt, &vd->mailbox_resp_queues[i].dest_queue, list_entry) { list_del(&cur->list_entry); kfree(cur); } spin_unlock_irqrestore(&vd->mailbox_resp_queues[i].lock, flags); } } static void gxp_mailbox_manager_set_ops(struct gxp_mailbox_manager *mgr) { mgr->allocate_mailbox = gxp_mailbox_manager_allocate_mailbox; mgr->release_mailbox = gxp_mailbox_release; mgr->reset_mailbox = gxp_mailbox_reset; mgr->execute_cmd = gxp_mailbox_manager_execute_cmd; mgr->execute_cmd_async = gxp_mailbox_manager_execute_cmd_async; mgr->wait_async_resp = gxp_mailbox_manager_wait_async_resp; mgr->release_unconsumed_async_resps = gxp_mailbox_manager_release_unconsumed_async_resps; } void gxp_mailbox_init(struct gxp_mailbox_manager *mgr) { gxp_mailbox_manager_set_ops(mgr); } int gxp_mailbox_init_consume_responses(struct gxp_mailbox *mailbox) { mailbox->cur_seq = 0; init_waitqueue_head(&mailbox->wait_list_waitq); INIT_LIST_HEAD(&mailbox->wait_list); return 0; } void gxp_mailbox_release_consume_responses(struct gxp_mailbox *mailbox) { struct gxp_mailbox_wait_list *cur, *nxt; struct gxp_async_response *async_resp; struct list_head resps_to_flush; unsigned long flags; /* * At this point only async responses should be pending. Flush them all * from the `wait_list` at once so any remaining timeout workers * waiting on `wait_list_lock` will know their responses have been * handled already. */ INIT_LIST_HEAD(&resps_to_flush); mutex_lock(&mailbox->wait_list_lock); list_for_each_entry_safe (cur, nxt, &mailbox->wait_list, list) { list_del(&cur->list); if (cur->is_async) { list_add_tail(&cur->list, &resps_to_flush); /* * Clear the response's destination queue so that if the * timeout worker is running, it won't try to process * this response after `wait_list_lock` is released. */ async_resp = container_of( cur->resp, struct gxp_async_response, resp); spin_lock_irqsave(async_resp->dest_queue_lock, flags); async_resp->dest_queue = NULL; spin_unlock_irqrestore(async_resp->dest_queue_lock, flags); } else { dev_warn( mailbox->gxp->dev, "Unexpected synchronous command pending on mailbox release\n"); kfree(cur); } } mutex_unlock(&mailbox->wait_list_lock); /* * Cancel the timeout timer of and free any responses that were still in * the `wait_list` above. */ list_for_each_entry_safe (cur, nxt, &resps_to_flush, list) { list_del(&cur->list); async_resp = container_of(cur->resp, struct gxp_async_response, resp); cancel_delayed_work_sync(&async_resp->timeout_work); kfree(async_resp); kfree(cur); } } void gxp_mailbox_consume_responses(struct gxp_mailbox *mailbox) { struct gxp_response *responses; u32 i; u32 count = 0; /* fetch responses and bump RESP_QUEUE_HEAD */ responses = gxp_mailbox_fetch_responses(mailbox, &count); if (IS_ERR(responses)) { dev_err(mailbox->gxp->dev, "GXP Mailbox failed on fetching responses: %ld", PTR_ERR(responses)); return; } for (i = 0; i < count; i++) gxp_mailbox_handle_response(mailbox, &responses[i]); /* * Responses handled, wake up threads that are waiting for a response. */ wake_up(&mailbox->wait_list_waitq); kfree(responses); }