Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 | // SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2011-2017, The Linux Foundation
*/
#include <linux/errno.h>
#include "slimbus.h"
/**
* slim_ctrl_clk_pause() - Called by slimbus controller to enter/exit
* 'clock pause'
* @ctrl: controller requesting bus to be paused or woken up
* @wakeup: Wakeup this controller from clock pause.
* @restart: Restart time value per spec used for clock pause. This value
* isn't used when controller is to be woken up.
*
* Slimbus specification needs this sequence to turn-off clocks for the bus.
* The sequence involves sending 3 broadcast messages (reconfiguration
* sequence) to inform all devices on the bus.
* To exit clock-pause, controller typically wakes up active framer device.
* This API executes clock pause reconfiguration sequence if wakeup is false.
* If wakeup is true, controller's wakeup is called.
* For entering clock-pause, -EBUSY is returned if a message txn in pending.
*/
int slim_ctrl_clk_pause(struct slim_controller *ctrl, bool wakeup, u8 restart)
{
int i, ret = 0;
unsigned long flags;
struct slim_sched *sched = &ctrl->sched;
struct slim_val_inf msg = {0, 0, NULL, NULL};
DEFINE_SLIM_BCAST_TXN(txn, SLIM_MSG_MC_BEGIN_RECONFIGURATION,
3, SLIM_LA_MANAGER, &msg);
if (wakeup == false && restart > SLIM_CLK_UNSPECIFIED)
return -EINVAL;
mutex_lock(&sched->m_reconf);
if (wakeup) {
if (sched->clk_state == SLIM_CLK_ACTIVE) {
mutex_unlock(&sched->m_reconf);
return 0;
}
/*
* Fine-tune calculation based on clock gear,
* message-bandwidth after bandwidth management
*/
ret = wait_for_completion_timeout(&sched->pause_comp,
msecs_to_jiffies(100));
if (!ret) {
mutex_unlock(&sched->m_reconf);
pr_err("Previous clock pause did not finish");
return -ETIMEDOUT;
}
ret = 0;
/*
* Slimbus framework will call controller wakeup
* Controller should make sure that it sets active framer
* out of clock pause
*/
if (sched->clk_state == SLIM_CLK_PAUSED && ctrl->wakeup)
ret = ctrl->wakeup(ctrl);
if (!ret)
sched->clk_state = SLIM_CLK_ACTIVE;
mutex_unlock(&sched->m_reconf);
return ret;
}
/* already paused */
if (ctrl->sched.clk_state == SLIM_CLK_PAUSED) {
mutex_unlock(&sched->m_reconf);
return 0;
}
spin_lock_irqsave(&ctrl->txn_lock, flags);
for (i = 0; i < SLIM_MAX_TIDS; i++) {
/* Pending response for a message */
if (idr_find(&ctrl->tid_idr, i)) {
spin_unlock_irqrestore(&ctrl->txn_lock, flags);
mutex_unlock(&sched->m_reconf);
return -EBUSY;
}
}
spin_unlock_irqrestore(&ctrl->txn_lock, flags);
sched->clk_state = SLIM_CLK_ENTERING_PAUSE;
/* clock pause sequence */
ret = slim_do_transfer(ctrl, &txn);
if (ret)
goto clk_pause_ret;
txn.mc = SLIM_MSG_MC_NEXT_PAUSE_CLOCK;
txn.rl = 4;
msg.num_bytes = 1;
msg.wbuf = &restart;
ret = slim_do_transfer(ctrl, &txn);
if (ret)
goto clk_pause_ret;
txn.mc = SLIM_MSG_MC_RECONFIGURE_NOW;
txn.rl = 3;
msg.num_bytes = 1;
msg.wbuf = NULL;
ret = slim_do_transfer(ctrl, &txn);
clk_pause_ret:
if (ret) {
sched->clk_state = SLIM_CLK_ACTIVE;
} else {
sched->clk_state = SLIM_CLK_PAUSED;
complete(&sched->pause_comp);
}
mutex_unlock(&sched->m_reconf);
return ret;
}
EXPORT_SYMBOL_GPL(slim_ctrl_clk_pause);
|