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