| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541 |
- // SPDX-License-Identifier: GPL-2.0-only
- /*
- * Copyright (C) 2020-21 Intel Corporation.
- */
- #include "iosm_ipc_protocol.h"
- #include "iosm_ipc_protocol_ops.h"
- /* Get the next free message element.*/
- static union ipc_mem_msg_entry *
- ipc_protocol_free_msg_get(struct iosm_protocol *ipc_protocol, int *index)
- {
- u32 head = le32_to_cpu(ipc_protocol->p_ap_shm->msg_head);
- u32 new_head = (head + 1) % IPC_MEM_MSG_ENTRIES;
- union ipc_mem_msg_entry *msg;
- if (new_head == le32_to_cpu(ipc_protocol->p_ap_shm->msg_tail)) {
- dev_err(ipc_protocol->dev, "message ring is full");
- return NULL;
- }
- /* Get the pointer to the next free message element,
- * reset the fields and mark is as invalid.
- */
- msg = &ipc_protocol->p_ap_shm->msg_ring[head];
- memset(msg, 0, sizeof(*msg));
- /* return index in message ring */
- *index = head;
- return msg;
- }
- /* Updates the message ring Head pointer */
- void ipc_protocol_msg_hp_update(struct iosm_imem *ipc_imem)
- {
- struct iosm_protocol *ipc_protocol = ipc_imem->ipc_protocol;
- u32 head = le32_to_cpu(ipc_protocol->p_ap_shm->msg_head);
- u32 new_head = (head + 1) % IPC_MEM_MSG_ENTRIES;
- /* Update head pointer and fire doorbell. */
- ipc_protocol->p_ap_shm->msg_head = cpu_to_le32(new_head);
- ipc_protocol->old_msg_tail =
- le32_to_cpu(ipc_protocol->p_ap_shm->msg_tail);
- ipc_pm_signal_hpda_doorbell(&ipc_protocol->pm, IPC_HP_MR, false);
- }
- /* Allocate and prepare a OPEN_PIPE message.
- * This also allocates the memory for the new TDR structure and
- * updates the pipe structure referenced in the preparation arguments.
- */
- static int ipc_protocol_msg_prepipe_open(struct iosm_protocol *ipc_protocol,
- union ipc_msg_prep_args *args)
- {
- int index;
- union ipc_mem_msg_entry *msg =
- ipc_protocol_free_msg_get(ipc_protocol, &index);
- struct ipc_pipe *pipe = args->pipe_open.pipe;
- struct ipc_protocol_td *tdr;
- struct sk_buff **skbr;
- if (!msg) {
- dev_err(ipc_protocol->dev, "failed to get free message");
- return -EIO;
- }
- /* Allocate the skbuf elements for the skbuf which are on the way.
- * SKB ring is internal memory allocation for driver. No need to
- * re-calculate the start and end addresses.
- */
- skbr = kcalloc(pipe->nr_of_entries, sizeof(*skbr), GFP_ATOMIC);
- if (!skbr)
- return -ENOMEM;
- /* Allocate the transfer descriptors for the pipe. */
- tdr = dma_alloc_coherent(&ipc_protocol->pcie->pci->dev,
- pipe->nr_of_entries * sizeof(*tdr),
- &pipe->phy_tdr_start, GFP_ATOMIC);
- if (!tdr) {
- kfree(skbr);
- dev_err(ipc_protocol->dev, "tdr alloc error");
- return -ENOMEM;
- }
- pipe->max_nr_of_queued_entries = pipe->nr_of_entries - 1;
- pipe->nr_of_queued_entries = 0;
- pipe->tdr_start = tdr;
- pipe->skbr_start = skbr;
- pipe->old_tail = 0;
- ipc_protocol->p_ap_shm->head_array[pipe->pipe_nr] = 0;
- msg->open_pipe.type_of_message = IPC_MEM_MSG_OPEN_PIPE;
- msg->open_pipe.pipe_nr = pipe->pipe_nr;
- msg->open_pipe.tdr_addr = cpu_to_le64(pipe->phy_tdr_start);
- msg->open_pipe.tdr_entries = cpu_to_le16(pipe->nr_of_entries);
- msg->open_pipe.accumulation_backoff =
- cpu_to_le32(pipe->accumulation_backoff);
- msg->open_pipe.irq_vector = cpu_to_le32(pipe->irq);
- return index;
- }
- static int ipc_protocol_msg_prepipe_close(struct iosm_protocol *ipc_protocol,
- union ipc_msg_prep_args *args)
- {
- int index = -1;
- union ipc_mem_msg_entry *msg =
- ipc_protocol_free_msg_get(ipc_protocol, &index);
- struct ipc_pipe *pipe = args->pipe_close.pipe;
- if (!msg)
- return -EIO;
- msg->close_pipe.type_of_message = IPC_MEM_MSG_CLOSE_PIPE;
- msg->close_pipe.pipe_nr = pipe->pipe_nr;
- dev_dbg(ipc_protocol->dev, "IPC_MEM_MSG_CLOSE_PIPE(pipe_nr=%d)",
- msg->close_pipe.pipe_nr);
- return index;
- }
- static int ipc_protocol_msg_prep_sleep(struct iosm_protocol *ipc_protocol,
- union ipc_msg_prep_args *args)
- {
- int index = -1;
- union ipc_mem_msg_entry *msg =
- ipc_protocol_free_msg_get(ipc_protocol, &index);
- if (!msg) {
- dev_err(ipc_protocol->dev, "failed to get free message");
- return -EIO;
- }
- /* Prepare and send the host sleep message to CP to enter or exit D3. */
- msg->host_sleep.type_of_message = IPC_MEM_MSG_SLEEP;
- msg->host_sleep.target = args->sleep.target; /* 0=host, 1=device */
- /* state; 0=enter, 1=exit 2=enter w/o protocol */
- msg->host_sleep.state = args->sleep.state;
- dev_dbg(ipc_protocol->dev, "IPC_MEM_MSG_SLEEP(target=%d; state=%d)",
- msg->host_sleep.target, msg->host_sleep.state);
- return index;
- }
- static int ipc_protocol_msg_prep_feature_set(struct iosm_protocol *ipc_protocol,
- union ipc_msg_prep_args *args)
- {
- int index = -1;
- union ipc_mem_msg_entry *msg =
- ipc_protocol_free_msg_get(ipc_protocol, &index);
- if (!msg) {
- dev_err(ipc_protocol->dev, "failed to get free message");
- return -EIO;
- }
- msg->feature_set.type_of_message = IPC_MEM_MSG_FEATURE_SET;
- msg->feature_set.reset_enable = args->feature_set.reset_enable <<
- RESET_BIT;
- dev_dbg(ipc_protocol->dev, "IPC_MEM_MSG_FEATURE_SET(reset_enable=%d)",
- msg->feature_set.reset_enable >> RESET_BIT);
- return index;
- }
- /* Processes the message consumed by CP. */
- bool ipc_protocol_msg_process(struct iosm_imem *ipc_imem, int irq)
- {
- struct iosm_protocol *ipc_protocol = ipc_imem->ipc_protocol;
- struct ipc_rsp **rsp_ring = ipc_protocol->rsp_ring;
- bool msg_processed = false;
- u32 i;
- if (le32_to_cpu(ipc_protocol->p_ap_shm->msg_tail) >=
- IPC_MEM_MSG_ENTRIES) {
- dev_err(ipc_protocol->dev, "msg_tail out of range: %d",
- le32_to_cpu(ipc_protocol->p_ap_shm->msg_tail));
- return msg_processed;
- }
- if (irq != IMEM_IRQ_DONT_CARE &&
- irq != ipc_protocol->p_ap_shm->ci.msg_irq_vector)
- return msg_processed;
- for (i = ipc_protocol->old_msg_tail;
- i != le32_to_cpu(ipc_protocol->p_ap_shm->msg_tail);
- i = (i + 1) % IPC_MEM_MSG_ENTRIES) {
- union ipc_mem_msg_entry *msg =
- &ipc_protocol->p_ap_shm->msg_ring[i];
- dev_dbg(ipc_protocol->dev, "msg[%d]: type=%u status=%d", i,
- msg->common.type_of_message,
- msg->common.completion_status);
- /* Update response with status and wake up waiting requestor */
- if (rsp_ring[i]) {
- rsp_ring[i]->status =
- le32_to_cpu(msg->common.completion_status);
- complete(&rsp_ring[i]->completion);
- rsp_ring[i] = NULL;
- }
- msg_processed = true;
- }
- ipc_protocol->old_msg_tail = i;
- return msg_processed;
- }
- /* Sends data from UL list to CP for the provided pipe by updating the Head
- * pointer of given pipe.
- */
- bool ipc_protocol_ul_td_send(struct iosm_protocol *ipc_protocol,
- struct ipc_pipe *pipe,
- struct sk_buff_head *p_ul_list)
- {
- struct ipc_protocol_td *td;
- bool hpda_pending = false;
- struct sk_buff *skb;
- s32 free_elements;
- u32 head;
- u32 tail;
- if (!ipc_protocol->p_ap_shm) {
- dev_err(ipc_protocol->dev, "driver is not initialized");
- return false;
- }
- /* Get head and tail of the td list and calculate
- * the number of free elements.
- */
- head = le32_to_cpu(ipc_protocol->p_ap_shm->head_array[pipe->pipe_nr]);
- tail = pipe->old_tail;
- while (!skb_queue_empty(p_ul_list)) {
- if (head < tail)
- free_elements = tail - head - 1;
- else
- free_elements =
- pipe->nr_of_entries - head + ((s32)tail - 1);
- if (free_elements <= 0) {
- dev_dbg(ipc_protocol->dev,
- "no free td elements for UL pipe %d",
- pipe->pipe_nr);
- break;
- }
- /* Get the td address. */
- td = &pipe->tdr_start[head];
- /* Take the first element of the uplink list and add it
- * to the td list.
- */
- skb = skb_dequeue(p_ul_list);
- if (WARN_ON(!skb))
- break;
- /* Save the reference to the uplink skbuf. */
- pipe->skbr_start[head] = skb;
- td->buffer.address = IPC_CB(skb)->mapping;
- td->scs = cpu_to_le32(skb->len) & cpu_to_le32(SIZE_MASK);
- td->next = 0;
- pipe->nr_of_queued_entries++;
- /* Calculate the new head and save it. */
- head++;
- if (head >= pipe->nr_of_entries)
- head = 0;
- ipc_protocol->p_ap_shm->head_array[pipe->pipe_nr] =
- cpu_to_le32(head);
- }
- if (pipe->old_head != head) {
- dev_dbg(ipc_protocol->dev, "New UL TDs Pipe:%d", pipe->pipe_nr);
- pipe->old_head = head;
- /* Trigger doorbell because of pending UL packets. */
- hpda_pending = true;
- }
- return hpda_pending;
- }
- /* Checks for Tail pointer update from CP and returns the data as SKB. */
- struct sk_buff *ipc_protocol_ul_td_process(struct iosm_protocol *ipc_protocol,
- struct ipc_pipe *pipe)
- {
- struct ipc_protocol_td *p_td = &pipe->tdr_start[pipe->old_tail];
- struct sk_buff *skb = pipe->skbr_start[pipe->old_tail];
- pipe->nr_of_queued_entries--;
- pipe->old_tail++;
- if (pipe->old_tail >= pipe->nr_of_entries)
- pipe->old_tail = 0;
- if (!p_td->buffer.address) {
- dev_err(ipc_protocol->dev, "Td buffer address is NULL");
- return NULL;
- }
- if (p_td->buffer.address != IPC_CB(skb)->mapping) {
- dev_err(ipc_protocol->dev,
- "pipe %d: invalid buf_addr or skb_data",
- pipe->pipe_nr);
- return NULL;
- }
- return skb;
- }
- /* Allocates an SKB for CP to send data and updates the Head Pointer
- * of the given Pipe#.
- */
- bool ipc_protocol_dl_td_prepare(struct iosm_protocol *ipc_protocol,
- struct ipc_pipe *pipe)
- {
- struct ipc_protocol_td *td;
- dma_addr_t mapping = 0;
- u32 head, new_head;
- struct sk_buff *skb;
- u32 tail;
- /* Get head and tail of the td list and calculate
- * the number of free elements.
- */
- head = le32_to_cpu(ipc_protocol->p_ap_shm->head_array[pipe->pipe_nr]);
- tail = le32_to_cpu(ipc_protocol->p_ap_shm->tail_array[pipe->pipe_nr]);
- new_head = head + 1;
- if (new_head >= pipe->nr_of_entries)
- new_head = 0;
- if (new_head == tail)
- return false;
- /* Get the td address. */
- td = &pipe->tdr_start[head];
- /* Allocate the skbuf for the descriptor. */
- skb = ipc_pcie_alloc_skb(ipc_protocol->pcie, pipe->buf_size, GFP_ATOMIC,
- &mapping, DMA_FROM_DEVICE,
- IPC_MEM_DL_ETH_OFFSET);
- if (!skb)
- return false;
- td->buffer.address = mapping;
- td->scs = cpu_to_le32(pipe->buf_size) & cpu_to_le32(SIZE_MASK);
- td->next = 0;
- /* store the new head value. */
- ipc_protocol->p_ap_shm->head_array[pipe->pipe_nr] =
- cpu_to_le32(new_head);
- /* Save the reference to the skbuf. */
- pipe->skbr_start[head] = skb;
- pipe->nr_of_queued_entries++;
- return true;
- }
- /* Processes DL TD's */
- struct sk_buff *ipc_protocol_dl_td_process(struct iosm_protocol *ipc_protocol,
- struct ipc_pipe *pipe)
- {
- struct ipc_protocol_td *p_td;
- struct sk_buff *skb;
- if (!pipe->tdr_start)
- return NULL;
- /* Copy the reference to the downlink buffer. */
- p_td = &pipe->tdr_start[pipe->old_tail];
- skb = pipe->skbr_start[pipe->old_tail];
- /* Reset the ring elements. */
- pipe->skbr_start[pipe->old_tail] = NULL;
- pipe->nr_of_queued_entries--;
- pipe->old_tail++;
- if (pipe->old_tail >= pipe->nr_of_entries)
- pipe->old_tail = 0;
- if (!skb) {
- dev_err(ipc_protocol->dev, "skb is null");
- goto ret;
- } else if (!p_td->buffer.address) {
- dev_err(ipc_protocol->dev, "td/buffer address is null");
- ipc_pcie_kfree_skb(ipc_protocol->pcie, skb);
- skb = NULL;
- goto ret;
- }
- if (p_td->buffer.address != IPC_CB(skb)->mapping) {
- dev_err(ipc_protocol->dev, "invalid buf=%llx or skb=%p",
- (unsigned long long)p_td->buffer.address, skb->data);
- ipc_pcie_kfree_skb(ipc_protocol->pcie, skb);
- skb = NULL;
- goto ret;
- } else if ((le32_to_cpu(p_td->scs) & SIZE_MASK) > pipe->buf_size) {
- dev_err(ipc_protocol->dev, "invalid buffer size %d > %d",
- le32_to_cpu(p_td->scs) & SIZE_MASK,
- pipe->buf_size);
- ipc_pcie_kfree_skb(ipc_protocol->pcie, skb);
- skb = NULL;
- goto ret;
- } else if (le32_to_cpu(p_td->scs) >> COMPLETION_STATUS ==
- IPC_MEM_TD_CS_ABORT) {
- /* Discard aborted buffers. */
- dev_dbg(ipc_protocol->dev, "discard 'aborted' buffers");
- ipc_pcie_kfree_skb(ipc_protocol->pcie, skb);
- skb = NULL;
- goto ret;
- }
- /* Set the length field in skbuf. */
- skb_put(skb, le32_to_cpu(p_td->scs) & SIZE_MASK);
- ret:
- return skb;
- }
- void ipc_protocol_get_head_tail_index(struct iosm_protocol *ipc_protocol,
- struct ipc_pipe *pipe, u32 *head,
- u32 *tail)
- {
- struct ipc_protocol_ap_shm *ipc_ap_shm = ipc_protocol->p_ap_shm;
- if (head)
- *head = le32_to_cpu(ipc_ap_shm->head_array[pipe->pipe_nr]);
- if (tail)
- *tail = le32_to_cpu(ipc_ap_shm->tail_array[pipe->pipe_nr]);
- }
- /* Frees the TDs given to CP. */
- void ipc_protocol_pipe_cleanup(struct iosm_protocol *ipc_protocol,
- struct ipc_pipe *pipe)
- {
- struct sk_buff *skb;
- u32 head;
- u32 tail;
- /* Get the start and the end of the buffer list. */
- head = le32_to_cpu(ipc_protocol->p_ap_shm->head_array[pipe->pipe_nr]);
- tail = pipe->old_tail;
- /* Reset tail and head to 0. */
- ipc_protocol->p_ap_shm->tail_array[pipe->pipe_nr] = 0;
- ipc_protocol->p_ap_shm->head_array[pipe->pipe_nr] = 0;
- /* Free pending uplink and downlink buffers. */
- if (pipe->skbr_start) {
- while (head != tail) {
- /* Get the reference to the skbuf,
- * which is on the way and free it.
- */
- skb = pipe->skbr_start[tail];
- if (skb)
- ipc_pcie_kfree_skb(ipc_protocol->pcie, skb);
- tail++;
- if (tail >= pipe->nr_of_entries)
- tail = 0;
- }
- kfree(pipe->skbr_start);
- pipe->skbr_start = NULL;
- }
- pipe->old_tail = 0;
- /* Free and reset the td and skbuf circular buffers. kfree is save! */
- if (pipe->tdr_start) {
- dma_free_coherent(&ipc_protocol->pcie->pci->dev,
- sizeof(*pipe->tdr_start) * pipe->nr_of_entries,
- pipe->tdr_start, pipe->phy_tdr_start);
- pipe->tdr_start = NULL;
- }
- }
- enum ipc_mem_device_ipc_state ipc_protocol_get_ipc_status(struct iosm_protocol
- *ipc_protocol)
- {
- return (enum ipc_mem_device_ipc_state)
- le32_to_cpu(ipc_protocol->p_ap_shm->device_info.ipc_status);
- }
- enum ipc_mem_exec_stage
- ipc_protocol_get_ap_exec_stage(struct iosm_protocol *ipc_protocol)
- {
- return le32_to_cpu(ipc_protocol->p_ap_shm->device_info.execution_stage);
- }
- int ipc_protocol_msg_prep(struct iosm_imem *ipc_imem,
- enum ipc_msg_prep_type msg_type,
- union ipc_msg_prep_args *args)
- {
- struct iosm_protocol *ipc_protocol = ipc_imem->ipc_protocol;
- switch (msg_type) {
- case IPC_MSG_PREP_SLEEP:
- return ipc_protocol_msg_prep_sleep(ipc_protocol, args);
- case IPC_MSG_PREP_PIPE_OPEN:
- return ipc_protocol_msg_prepipe_open(ipc_protocol, args);
- case IPC_MSG_PREP_PIPE_CLOSE:
- return ipc_protocol_msg_prepipe_close(ipc_protocol, args);
- case IPC_MSG_PREP_FEATURE_SET:
- return ipc_protocol_msg_prep_feature_set(ipc_protocol, args);
- /* Unsupported messages in protocol */
- case IPC_MSG_PREP_MAP:
- case IPC_MSG_PREP_UNMAP:
- default:
- dev_err(ipc_protocol->dev,
- "unsupported message type: %d in protocol", msg_type);
- return -EINVAL;
- }
- }
- u32
- ipc_protocol_pm_dev_get_sleep_notification(struct iosm_protocol *ipc_protocol)
- {
- struct ipc_protocol_ap_shm *ipc_ap_shm = ipc_protocol->p_ap_shm;
- return le32_to_cpu(ipc_ap_shm->device_info.device_sleep_notification);
- }
|