forked from Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
121 lines
3.2 KiB
121 lines
3.2 KiB
// 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);
|
|
|