linux/drivers/net/wwan/iosm/iosm_ipc_protocol.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Copyright (C) 2020-21 Intel Corporation.
   4 */
   5
   6#include "iosm_ipc_imem.h"
   7#include "iosm_ipc_protocol.h"
   8#include "iosm_ipc_protocol_ops.h"
   9#include "iosm_ipc_pm.h"
  10#include "iosm_ipc_task_queue.h"
  11
  12int ipc_protocol_tq_msg_send(struct iosm_protocol *ipc_protocol,
  13                             enum ipc_msg_prep_type msg_type,
  14                             union ipc_msg_prep_args *prep_args,
  15                             struct ipc_rsp *response)
  16{
  17        int index = ipc_protocol_msg_prep(ipc_protocol->imem, msg_type,
  18                                          prep_args);
  19
  20        /* Store reference towards caller specified response in response ring
  21         * and signal CP
  22         */
  23        if (index >= 0 && index < IPC_MEM_MSG_ENTRIES) {
  24                ipc_protocol->rsp_ring[index] = response;
  25                ipc_protocol_msg_hp_update(ipc_protocol->imem);
  26        }
  27
  28        return index;
  29}
  30
  31/* Callback for message send */
  32static int ipc_protocol_tq_msg_send_cb(struct iosm_imem *ipc_imem, int arg,
  33                                       void *msg, size_t size)
  34{
  35        struct ipc_call_msg_send_args *send_args = msg;
  36        struct iosm_protocol *ipc_protocol = ipc_imem->ipc_protocol;
  37
  38        return ipc_protocol_tq_msg_send(ipc_protocol, send_args->msg_type,
  39                                        send_args->prep_args,
  40                                        send_args->response);
  41}
  42
  43/* Remove reference to a response. This is typically used when a requestor timed
  44 * out and is no longer interested in the response.
  45 */
  46static int ipc_protocol_tq_msg_remove(struct iosm_imem *ipc_imem, int arg,
  47                                      void *msg, size_t size)
  48{
  49        struct iosm_protocol *ipc_protocol = ipc_imem->ipc_protocol;
  50
  51        ipc_protocol->rsp_ring[arg] = NULL;
  52        return 0;
  53}
  54
  55int ipc_protocol_msg_send(struct iosm_protocol *ipc_protocol,
  56                          enum ipc_msg_prep_type prep,
  57                          union ipc_msg_prep_args *prep_args)
  58{
  59        struct ipc_call_msg_send_args send_args;
  60        unsigned int exec_timeout;
  61        struct ipc_rsp response;
  62        int index;
  63
  64        exec_timeout = (ipc_protocol_get_ap_exec_stage(ipc_protocol) ==
  65                                        IPC_MEM_EXEC_STAGE_RUN ?
  66                                IPC_MSG_COMPLETE_RUN_DEFAULT_TIMEOUT :
  67                                IPC_MSG_COMPLETE_BOOT_DEFAULT_TIMEOUT);
  68
  69        /* Trap if called from non-preemptible context */
  70        might_sleep();
  71
  72        response.status = IPC_MEM_MSG_CS_INVALID;
  73        init_completion(&response.completion);
  74
  75        send_args.msg_type = prep;
  76        send_args.prep_args = prep_args;
  77        send_args.response = &response;
  78
  79        /* Allocate and prepare message to be sent in tasklet context.
  80         * A positive index returned form tasklet_call references the message
  81         * in case it needs to be cancelled when there is a timeout.
  82         */
  83        index = ipc_task_queue_send_task(ipc_protocol->imem,
  84                                         ipc_protocol_tq_msg_send_cb, 0,
  85                                         &send_args, 0, true);
  86
  87        if (index < 0) {
  88                dev_err(ipc_protocol->dev, "msg %d failed", prep);
  89                return index;
  90        }
  91
  92        /* Wait for the device to respond to the message */
  93        switch (wait_for_completion_timeout(&response.completion,
  94                                            msecs_to_jiffies(exec_timeout))) {
  95        case 0:
  96                /* Timeout, there was no response from the device.
  97                 * Remove the reference to the local response completion
  98                 * object as we are no longer interested in the response.
  99                 */
 100                ipc_task_queue_send_task(ipc_protocol->imem,
 101                                         ipc_protocol_tq_msg_remove, index,
 102                                         NULL, 0, true);
 103                dev_err(ipc_protocol->dev, "msg timeout");
 104                ipc_uevent_send(ipc_protocol->pcie->dev, UEVENT_MDM_TIMEOUT);
 105                break;
 106        default:
 107                /* We got a response in time; check completion status: */
 108                if (response.status != IPC_MEM_MSG_CS_SUCCESS) {
 109                        dev_err(ipc_protocol->dev,
 110                                "msg completion status error %d",
 111                                response.status);
 112                        return -EIO;
 113                }
 114        }
 115
 116        return 0;
 117}
 118
 119static int ipc_protocol_msg_send_host_sleep(struct iosm_protocol *ipc_protocol,
 120                                            u32 state)
 121{
 122        union ipc_msg_prep_args prep_args = {
 123                .sleep.target = 0,
 124                .sleep.state = state,
 125        };
 126
 127        return ipc_protocol_msg_send(ipc_protocol, IPC_MSG_PREP_SLEEP,
 128                                     &prep_args);
 129}
 130
 131void ipc_protocol_doorbell_trigger(struct iosm_protocol *ipc_protocol,
 132                                   u32 identifier)
 133{
 134        ipc_pm_signal_hpda_doorbell(&ipc_protocol->pm, identifier, true);
 135}
 136
 137bool ipc_protocol_pm_dev_sleep_handle(struct iosm_protocol *ipc_protocol)
 138{
 139        u32 ipc_status = ipc_protocol_get_ipc_status(ipc_protocol);
 140        u32 requested;
 141
 142        if (ipc_status != IPC_MEM_DEVICE_IPC_RUNNING) {
 143                dev_err(ipc_protocol->dev,
 144                        "irq ignored, CP IPC state is %d, should be RUNNING",
 145                        ipc_status);
 146
 147                /* Stop further processing. */
 148                return false;
 149        }
 150
 151        /* Get a copy of the requested PM state by the device and the local
 152         * device PM state.
 153         */
 154        requested = ipc_protocol_pm_dev_get_sleep_notification(ipc_protocol);
 155
 156        return ipc_pm_dev_slp_notification(&ipc_protocol->pm, requested);
 157}
 158
 159static int ipc_protocol_tq_wakeup_dev_slp(struct iosm_imem *ipc_imem, int arg,
 160                                          void *msg, size_t size)
 161{
 162        struct iosm_pm *ipc_pm = &ipc_imem->ipc_protocol->pm;
 163
 164        /* Wakeup from device sleep if it is not ACTIVE */
 165        ipc_pm_trigger(ipc_pm, IPC_PM_UNIT_HS, true);
 166
 167        ipc_pm_trigger(ipc_pm, IPC_PM_UNIT_HS, false);
 168
 169        return 0;
 170}
 171
 172void ipc_protocol_s2idle_sleep(struct iosm_protocol *ipc_protocol, bool sleep)
 173{
 174        ipc_pm_set_s2idle_sleep(&ipc_protocol->pm, sleep);
 175}
 176
 177bool ipc_protocol_suspend(struct iosm_protocol *ipc_protocol)
 178{
 179        if (!ipc_pm_prepare_host_sleep(&ipc_protocol->pm))
 180                goto err;
 181
 182        ipc_task_queue_send_task(ipc_protocol->imem,
 183                                 ipc_protocol_tq_wakeup_dev_slp, 0, NULL, 0,
 184                                 true);
 185
 186        if (!ipc_pm_wait_for_device_active(&ipc_protocol->pm)) {
 187                ipc_uevent_send(ipc_protocol->pcie->dev, UEVENT_MDM_TIMEOUT);
 188                goto err;
 189        }
 190
 191        /* Send the sleep message for sync sys calls. */
 192        dev_dbg(ipc_protocol->dev, "send TARGET_HOST, ENTER_SLEEP");
 193        if (ipc_protocol_msg_send_host_sleep(ipc_protocol,
 194                                             IPC_HOST_SLEEP_ENTER_SLEEP)) {
 195                /* Sending ENTER_SLEEP message failed, we are still active */
 196                ipc_protocol->pm.host_pm_state = IPC_MEM_HOST_PM_ACTIVE;
 197                goto err;
 198        }
 199
 200        ipc_protocol->pm.host_pm_state = IPC_MEM_HOST_PM_SLEEP;
 201        return true;
 202err:
 203        return false;
 204}
 205
 206bool ipc_protocol_resume(struct iosm_protocol *ipc_protocol)
 207{
 208        if (!ipc_pm_prepare_host_active(&ipc_protocol->pm))
 209                return false;
 210
 211        dev_dbg(ipc_protocol->dev, "send TARGET_HOST, EXIT_SLEEP");
 212        if (ipc_protocol_msg_send_host_sleep(ipc_protocol,
 213                                             IPC_HOST_SLEEP_EXIT_SLEEP)) {
 214                ipc_protocol->pm.host_pm_state = IPC_MEM_HOST_PM_SLEEP;
 215                return false;
 216        }
 217
 218        ipc_protocol->pm.host_pm_state = IPC_MEM_HOST_PM_ACTIVE;
 219
 220        return true;
 221}
 222
 223struct iosm_protocol *ipc_protocol_init(struct iosm_imem *ipc_imem)
 224{
 225        struct iosm_protocol *ipc_protocol =
 226                kzalloc(sizeof(*ipc_protocol), GFP_KERNEL);
 227        struct ipc_protocol_context_info *p_ci;
 228        u64 addr;
 229
 230        if (!ipc_protocol)
 231                return NULL;
 232
 233        ipc_protocol->dev = ipc_imem->dev;
 234        ipc_protocol->pcie = ipc_imem->pcie;
 235        ipc_protocol->imem = ipc_imem;
 236        ipc_protocol->p_ap_shm = NULL;
 237        ipc_protocol->phy_ap_shm = 0;
 238
 239        ipc_protocol->old_msg_tail = 0;
 240
 241        ipc_protocol->p_ap_shm =
 242                dma_alloc_coherent(&ipc_protocol->pcie->pci->dev,
 243                                   sizeof(*ipc_protocol->p_ap_shm),
 244                                   &ipc_protocol->phy_ap_shm, GFP_KERNEL);
 245
 246        if (!ipc_protocol->p_ap_shm) {
 247                dev_err(ipc_protocol->dev, "pci shm alloc error");
 248                kfree(ipc_protocol);
 249                return NULL;
 250        }
 251
 252        /* Prepare the context info for CP. */
 253        addr = ipc_protocol->phy_ap_shm;
 254        p_ci = &ipc_protocol->p_ap_shm->ci;
 255        p_ci->device_info_addr =
 256                addr + offsetof(struct ipc_protocol_ap_shm, device_info);
 257        p_ci->head_array =
 258                addr + offsetof(struct ipc_protocol_ap_shm, head_array);
 259        p_ci->tail_array =
 260                addr + offsetof(struct ipc_protocol_ap_shm, tail_array);
 261        p_ci->msg_head = addr + offsetof(struct ipc_protocol_ap_shm, msg_head);
 262        p_ci->msg_tail = addr + offsetof(struct ipc_protocol_ap_shm, msg_tail);
 263        p_ci->msg_ring_addr =
 264                addr + offsetof(struct ipc_protocol_ap_shm, msg_ring);
 265        p_ci->msg_ring_entries = cpu_to_le16(IPC_MEM_MSG_ENTRIES);
 266        p_ci->msg_irq_vector = IPC_MSG_IRQ_VECTOR;
 267        p_ci->device_info_irq_vector = IPC_DEVICE_IRQ_VECTOR;
 268
 269        ipc_mmio_set_contex_info_addr(ipc_imem->mmio, addr);
 270
 271        ipc_pm_init(ipc_protocol);
 272
 273        return ipc_protocol;
 274}
 275
 276void ipc_protocol_deinit(struct iosm_protocol *proto)
 277{
 278        dma_free_coherent(&proto->pcie->pci->dev, sizeof(*proto->p_ap_shm),
 279                          proto->p_ap_shm, proto->phy_ap_shm);
 280
 281        ipc_pm_deinit(proto);
 282        kfree(proto);
 283}
 284