linux/drivers/net/wwan/iosm/iosm_ipc_pm.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Copyright (C) 2020-21 Intel Corporation.
   4 */
   5
   6#include "iosm_ipc_protocol.h"
   7
   8/* Timeout value in MS for the PM to wait for device to reach active state */
   9#define IPC_PM_ACTIVE_TIMEOUT_MS (500)
  10
  11/* Note that here "active" has the value 1, as compared to the enums
  12 * ipc_mem_host_pm_state or ipc_mem_dev_pm_state, where "active" is 0
  13 */
  14#define IPC_PM_SLEEP (0)
  15#define CONSUME_STATE (0)
  16#define IPC_PM_ACTIVE (1)
  17
  18void ipc_pm_signal_hpda_doorbell(struct iosm_pm *ipc_pm, u32 identifier,
  19                                 bool host_slp_check)
  20{
  21        if (host_slp_check && ipc_pm->host_pm_state != IPC_MEM_HOST_PM_ACTIVE &&
  22            ipc_pm->host_pm_state != IPC_MEM_HOST_PM_ACTIVE_WAIT) {
  23                ipc_pm->pending_hpda_update = true;
  24                dev_dbg(ipc_pm->dev,
  25                        "Pend HPDA update set. Host PM_State: %d identifier:%d",
  26                        ipc_pm->host_pm_state, identifier);
  27                return;
  28        }
  29
  30        if (!ipc_pm_trigger(ipc_pm, IPC_PM_UNIT_IRQ, true)) {
  31                ipc_pm->pending_hpda_update = true;
  32                dev_dbg(ipc_pm->dev, "Pending HPDA update set. identifier:%d",
  33                        identifier);
  34                return;
  35        }
  36        ipc_pm->pending_hpda_update = false;
  37
  38        /* Trigger the irq towards CP */
  39        ipc_cp_irq_hpda_update(ipc_pm->pcie, identifier);
  40
  41        ipc_pm_trigger(ipc_pm, IPC_PM_UNIT_IRQ, false);
  42}
  43
  44/* Wake up the device if it is in low power mode. */
  45static bool ipc_pm_link_activate(struct iosm_pm *ipc_pm)
  46{
  47        if (ipc_pm->cp_state == IPC_MEM_DEV_PM_ACTIVE)
  48                return true;
  49
  50        if (ipc_pm->cp_state == IPC_MEM_DEV_PM_SLEEP) {
  51                if (ipc_pm->ap_state == IPC_MEM_DEV_PM_SLEEP) {
  52                        /* Wake up the device. */
  53                        ipc_cp_irq_sleep_control(ipc_pm->pcie,
  54                                                 IPC_MEM_DEV_PM_WAKEUP);
  55                        ipc_pm->ap_state = IPC_MEM_DEV_PM_ACTIVE_WAIT;
  56
  57                        goto not_active;
  58                }
  59
  60                if (ipc_pm->ap_state == IPC_MEM_DEV_PM_ACTIVE_WAIT)
  61                        goto not_active;
  62
  63                return true;
  64        }
  65
  66not_active:
  67        /* link is not ready */
  68        return false;
  69}
  70
  71bool ipc_pm_wait_for_device_active(struct iosm_pm *ipc_pm)
  72{
  73        bool ret_val = false;
  74
  75        if (ipc_pm->ap_state != IPC_MEM_DEV_PM_ACTIVE) {
  76                /* Complete all memory stores before setting bit */
  77                smp_mb__before_atomic();
  78
  79                /* Wait for IPC_PM_ACTIVE_TIMEOUT_MS for Device sleep state
  80                 * machine to enter ACTIVE state.
  81                 */
  82                set_bit(0, &ipc_pm->host_sleep_pend);
  83
  84                /* Complete all memory stores after setting bit */
  85                smp_mb__after_atomic();
  86
  87                if (!wait_for_completion_interruptible_timeout
  88                   (&ipc_pm->host_sleep_complete,
  89                    msecs_to_jiffies(IPC_PM_ACTIVE_TIMEOUT_MS))) {
  90                        dev_err(ipc_pm->dev,
  91                                "PM timeout. Expected State:%d. Actual: %d",
  92                                IPC_MEM_DEV_PM_ACTIVE, ipc_pm->ap_state);
  93                        goto  active_timeout;
  94                }
  95        }
  96
  97        ret_val = true;
  98active_timeout:
  99        /* Complete all memory stores before clearing bit */
 100        smp_mb__before_atomic();
 101
 102        /* Reset the atomic variable in any case as device sleep
 103         * state machine change is no longer of interest.
 104         */
 105        clear_bit(0, &ipc_pm->host_sleep_pend);
 106
 107        /* Complete all memory stores after clearing bit */
 108        smp_mb__after_atomic();
 109
 110        return ret_val;
 111}
 112
 113static void ipc_pm_on_link_sleep(struct iosm_pm *ipc_pm)
 114{
 115        /* pending sleep ack and all conditions are cleared
 116         * -> signal SLEEP__ACK to CP
 117         */
 118        ipc_pm->cp_state = IPC_MEM_DEV_PM_SLEEP;
 119        ipc_pm->ap_state = IPC_MEM_DEV_PM_SLEEP;
 120
 121        ipc_cp_irq_sleep_control(ipc_pm->pcie, IPC_MEM_DEV_PM_SLEEP);
 122}
 123
 124static void ipc_pm_on_link_wake(struct iosm_pm *ipc_pm, bool ack)
 125{
 126        ipc_pm->ap_state = IPC_MEM_DEV_PM_ACTIVE;
 127
 128        if (ack) {
 129                ipc_pm->cp_state = IPC_MEM_DEV_PM_ACTIVE;
 130
 131                ipc_cp_irq_sleep_control(ipc_pm->pcie, IPC_MEM_DEV_PM_ACTIVE);
 132
 133                /* check the consume state !!! */
 134                if (test_bit(CONSUME_STATE, &ipc_pm->host_sleep_pend))
 135                        complete(&ipc_pm->host_sleep_complete);
 136        }
 137
 138        /* Check for pending HPDA update.
 139         * Pending HP update could be because of sending message was
 140         * put on hold due to Device sleep state or due to TD update
 141         * which could be because of Device Sleep and Host Sleep
 142         * states.
 143         */
 144        if (ipc_pm->pending_hpda_update &&
 145            ipc_pm->host_pm_state == IPC_MEM_HOST_PM_ACTIVE)
 146                ipc_pm_signal_hpda_doorbell(ipc_pm, IPC_HP_PM_TRIGGER, true);
 147}
 148
 149bool ipc_pm_trigger(struct iosm_pm *ipc_pm, enum ipc_pm_unit unit, bool active)
 150{
 151        union ipc_pm_cond old_cond;
 152        union ipc_pm_cond new_cond;
 153        bool link_active;
 154
 155        /* Save the current D3 state. */
 156        new_cond = ipc_pm->pm_cond;
 157        old_cond = ipc_pm->pm_cond;
 158
 159        /* Calculate the power state only in the runtime phase. */
 160        switch (unit) {
 161        case IPC_PM_UNIT_IRQ: /* CP irq */
 162                new_cond.irq = active;
 163                break;
 164
 165        case IPC_PM_UNIT_LINK: /* Device link state. */
 166                new_cond.link = active;
 167                break;
 168
 169        case IPC_PM_UNIT_HS: /* Host sleep trigger requires Link. */
 170                new_cond.hs = active;
 171                break;
 172
 173        default:
 174                break;
 175        }
 176
 177        /* Something changed ? */
 178        if (old_cond.raw == new_cond.raw) {
 179                /* Stay in the current PM state. */
 180                link_active = old_cond.link == IPC_PM_ACTIVE;
 181                goto ret;
 182        }
 183
 184        ipc_pm->pm_cond = new_cond;
 185
 186        if (new_cond.link)
 187                ipc_pm_on_link_wake(ipc_pm, unit == IPC_PM_UNIT_LINK);
 188        else if (unit == IPC_PM_UNIT_LINK)
 189                ipc_pm_on_link_sleep(ipc_pm);
 190
 191        if (old_cond.link == IPC_PM_SLEEP && new_cond.raw) {
 192                link_active = ipc_pm_link_activate(ipc_pm);
 193                goto ret;
 194        }
 195
 196        link_active = old_cond.link == IPC_PM_ACTIVE;
 197
 198ret:
 199        return link_active;
 200}
 201
 202bool ipc_pm_prepare_host_sleep(struct iosm_pm *ipc_pm)
 203{
 204        /* suspend not allowed if host_pm_state is not IPC_MEM_HOST_PM_ACTIVE */
 205        if (ipc_pm->host_pm_state != IPC_MEM_HOST_PM_ACTIVE) {
 206                dev_err(ipc_pm->dev, "host_pm_state=%d\tExpected to be: %d",
 207                        ipc_pm->host_pm_state, IPC_MEM_HOST_PM_ACTIVE);
 208                return false;
 209        }
 210
 211        ipc_pm->host_pm_state = IPC_MEM_HOST_PM_SLEEP_WAIT_D3;
 212
 213        return true;
 214}
 215
 216bool ipc_pm_prepare_host_active(struct iosm_pm *ipc_pm)
 217{
 218        if (ipc_pm->host_pm_state != IPC_MEM_HOST_PM_SLEEP) {
 219                dev_err(ipc_pm->dev, "host_pm_state=%d\tExpected to be: %d",
 220                        ipc_pm->host_pm_state, IPC_MEM_HOST_PM_SLEEP);
 221                return false;
 222        }
 223
 224        /* Sending Sleep Exit message to CP. Update the state */
 225        ipc_pm->host_pm_state = IPC_MEM_HOST_PM_ACTIVE_WAIT;
 226
 227        return true;
 228}
 229
 230void ipc_pm_set_s2idle_sleep(struct iosm_pm *ipc_pm, bool sleep)
 231{
 232        if (sleep) {
 233                ipc_pm->ap_state = IPC_MEM_DEV_PM_SLEEP;
 234                ipc_pm->cp_state = IPC_MEM_DEV_PM_SLEEP;
 235                ipc_pm->device_sleep_notification = IPC_MEM_DEV_PM_SLEEP;
 236        } else {
 237                ipc_pm->ap_state = IPC_MEM_DEV_PM_ACTIVE;
 238                ipc_pm->cp_state = IPC_MEM_DEV_PM_ACTIVE;
 239                ipc_pm->device_sleep_notification = IPC_MEM_DEV_PM_ACTIVE;
 240                ipc_pm->pm_cond.link = IPC_PM_ACTIVE;
 241        }
 242}
 243
 244bool ipc_pm_dev_slp_notification(struct iosm_pm *ipc_pm, u32 cp_pm_req)
 245{
 246        if (cp_pm_req == ipc_pm->device_sleep_notification)
 247                return false;
 248
 249        ipc_pm->device_sleep_notification = cp_pm_req;
 250
 251        /* Evaluate the PM request. */
 252        switch (ipc_pm->cp_state) {
 253        case IPC_MEM_DEV_PM_ACTIVE:
 254                switch (cp_pm_req) {
 255                case IPC_MEM_DEV_PM_ACTIVE:
 256                        break;
 257
 258                case IPC_MEM_DEV_PM_SLEEP:
 259                        /* Inform the PM that the device link can go down. */
 260                        ipc_pm_trigger(ipc_pm, IPC_PM_UNIT_LINK, false);
 261                        return true;
 262
 263                default:
 264                        dev_err(ipc_pm->dev,
 265                                "loc-pm=%d active: confused req-pm=%d",
 266                                ipc_pm->cp_state, cp_pm_req);
 267                        break;
 268                }
 269                break;
 270
 271        case IPC_MEM_DEV_PM_SLEEP:
 272                switch (cp_pm_req) {
 273                case IPC_MEM_DEV_PM_ACTIVE:
 274                        /* Inform the PM that the device link is active. */
 275                        ipc_pm_trigger(ipc_pm, IPC_PM_UNIT_LINK, true);
 276                        break;
 277
 278                case IPC_MEM_DEV_PM_SLEEP:
 279                        break;
 280
 281                default:
 282                        dev_err(ipc_pm->dev,
 283                                "loc-pm=%d sleep: confused req-pm=%d",
 284                                ipc_pm->cp_state, cp_pm_req);
 285                        break;
 286                }
 287                break;
 288
 289        default:
 290                dev_err(ipc_pm->dev, "confused loc-pm=%d, req-pm=%d",
 291                        ipc_pm->cp_state, cp_pm_req);
 292                break;
 293        }
 294
 295        return false;
 296}
 297
 298void ipc_pm_init(struct iosm_protocol *ipc_protocol)
 299{
 300        struct iosm_imem *ipc_imem = ipc_protocol->imem;
 301        struct iosm_pm *ipc_pm = &ipc_protocol->pm;
 302
 303        ipc_pm->pcie = ipc_imem->pcie;
 304        ipc_pm->dev = ipc_imem->dev;
 305
 306        ipc_pm->pm_cond.irq = IPC_PM_SLEEP;
 307        ipc_pm->pm_cond.hs = IPC_PM_SLEEP;
 308        ipc_pm->pm_cond.link = IPC_PM_ACTIVE;
 309
 310        ipc_pm->cp_state = IPC_MEM_DEV_PM_ACTIVE;
 311        ipc_pm->ap_state = IPC_MEM_DEV_PM_ACTIVE;
 312        ipc_pm->host_pm_state = IPC_MEM_HOST_PM_ACTIVE;
 313
 314        /* Create generic wait-for-completion handler for Host Sleep
 315         * and device sleep coordination.
 316         */
 317        init_completion(&ipc_pm->host_sleep_complete);
 318
 319        /* Complete all memory stores before clearing bit */
 320        smp_mb__before_atomic();
 321
 322        clear_bit(0, &ipc_pm->host_sleep_pend);
 323
 324        /* Complete all memory stores after clearing bit */
 325        smp_mb__after_atomic();
 326}
 327
 328void ipc_pm_deinit(struct iosm_protocol *proto)
 329{
 330        struct iosm_pm *ipc_pm = &proto->pm;
 331
 332        complete(&ipc_pm->host_sleep_complete);
 333}
 334