mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-11-01 00:48:50 +00:00
4b14e62ad3
Per SLIMbus specification, a reconfiguration sequence known as 'clock pause' needs to be broadcast over the bus while entering low- power mode. Clock-pause is initiated by the controller driver. To exit clock-pause, controller typically wakes up the framer device. Since wakeup precedure is controller-specific, framework calls it via controller's function pointer to invoke it. Signed-off-by: Sagar Dharia <sdharia@codeaurora.org> Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org> Reviwed-by: Mark Brown <broonie@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
121 lines
3.2 KiB
C
121 lines
3.2 KiB
C
// 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);
|