18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (c) 2011-2017, The Linux Foundation
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include <linux/errno.h>
78c2ecf20Sopenharmony_ci#include "slimbus.h"
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci/**
108c2ecf20Sopenharmony_ci * slim_ctrl_clk_pause() - Called by slimbus controller to enter/exit
118c2ecf20Sopenharmony_ci *			   'clock pause'
128c2ecf20Sopenharmony_ci * @ctrl: controller requesting bus to be paused or woken up
138c2ecf20Sopenharmony_ci * @wakeup: Wakeup this controller from clock pause.
148c2ecf20Sopenharmony_ci * @restart: Restart time value per spec used for clock pause. This value
158c2ecf20Sopenharmony_ci *	isn't used when controller is to be woken up.
168c2ecf20Sopenharmony_ci *
178c2ecf20Sopenharmony_ci * Slimbus specification needs this sequence to turn-off clocks for the bus.
188c2ecf20Sopenharmony_ci * The sequence involves sending 3 broadcast messages (reconfiguration
198c2ecf20Sopenharmony_ci * sequence) to inform all devices on the bus.
208c2ecf20Sopenharmony_ci * To exit clock-pause, controller typically wakes up active framer device.
218c2ecf20Sopenharmony_ci * This API executes clock pause reconfiguration sequence if wakeup is false.
228c2ecf20Sopenharmony_ci * If wakeup is true, controller's wakeup is called.
238c2ecf20Sopenharmony_ci * For entering clock-pause, -EBUSY is returned if a message txn in pending.
248c2ecf20Sopenharmony_ci */
258c2ecf20Sopenharmony_ciint slim_ctrl_clk_pause(struct slim_controller *ctrl, bool wakeup, u8 restart)
268c2ecf20Sopenharmony_ci{
278c2ecf20Sopenharmony_ci	int i, ret = 0;
288c2ecf20Sopenharmony_ci	unsigned long flags;
298c2ecf20Sopenharmony_ci	struct slim_sched *sched = &ctrl->sched;
308c2ecf20Sopenharmony_ci	struct slim_val_inf msg = {0, 0, NULL, NULL};
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci	DEFINE_SLIM_BCAST_TXN(txn, SLIM_MSG_MC_BEGIN_RECONFIGURATION,
338c2ecf20Sopenharmony_ci				3, SLIM_LA_MANAGER, &msg);
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci	if (wakeup == false && restart > SLIM_CLK_UNSPECIFIED)
368c2ecf20Sopenharmony_ci		return -EINVAL;
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci	mutex_lock(&sched->m_reconf);
398c2ecf20Sopenharmony_ci	if (wakeup) {
408c2ecf20Sopenharmony_ci		if (sched->clk_state == SLIM_CLK_ACTIVE) {
418c2ecf20Sopenharmony_ci			mutex_unlock(&sched->m_reconf);
428c2ecf20Sopenharmony_ci			return 0;
438c2ecf20Sopenharmony_ci		}
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci		/*
468c2ecf20Sopenharmony_ci		 * Fine-tune calculation based on clock gear,
478c2ecf20Sopenharmony_ci		 * message-bandwidth after bandwidth management
488c2ecf20Sopenharmony_ci		 */
498c2ecf20Sopenharmony_ci		ret = wait_for_completion_timeout(&sched->pause_comp,
508c2ecf20Sopenharmony_ci				msecs_to_jiffies(100));
518c2ecf20Sopenharmony_ci		if (!ret) {
528c2ecf20Sopenharmony_ci			mutex_unlock(&sched->m_reconf);
538c2ecf20Sopenharmony_ci			pr_err("Previous clock pause did not finish");
548c2ecf20Sopenharmony_ci			return -ETIMEDOUT;
558c2ecf20Sopenharmony_ci		}
568c2ecf20Sopenharmony_ci		ret = 0;
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci		/*
598c2ecf20Sopenharmony_ci		 * Slimbus framework will call controller wakeup
608c2ecf20Sopenharmony_ci		 * Controller should make sure that it sets active framer
618c2ecf20Sopenharmony_ci		 * out of clock pause
628c2ecf20Sopenharmony_ci		 */
638c2ecf20Sopenharmony_ci		if (sched->clk_state == SLIM_CLK_PAUSED && ctrl->wakeup)
648c2ecf20Sopenharmony_ci			ret = ctrl->wakeup(ctrl);
658c2ecf20Sopenharmony_ci		if (!ret)
668c2ecf20Sopenharmony_ci			sched->clk_state = SLIM_CLK_ACTIVE;
678c2ecf20Sopenharmony_ci		mutex_unlock(&sched->m_reconf);
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci		return ret;
708c2ecf20Sopenharmony_ci	}
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	/* already paused */
738c2ecf20Sopenharmony_ci	if (ctrl->sched.clk_state == SLIM_CLK_PAUSED) {
748c2ecf20Sopenharmony_ci		mutex_unlock(&sched->m_reconf);
758c2ecf20Sopenharmony_ci		return 0;
768c2ecf20Sopenharmony_ci	}
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ctrl->txn_lock, flags);
798c2ecf20Sopenharmony_ci	for (i = 0; i < SLIM_MAX_TIDS; i++) {
808c2ecf20Sopenharmony_ci		/* Pending response for a message */
818c2ecf20Sopenharmony_ci		if (idr_find(&ctrl->tid_idr, i)) {
828c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&ctrl->txn_lock, flags);
838c2ecf20Sopenharmony_ci			mutex_unlock(&sched->m_reconf);
848c2ecf20Sopenharmony_ci			return -EBUSY;
858c2ecf20Sopenharmony_ci		}
868c2ecf20Sopenharmony_ci	}
878c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ctrl->txn_lock, flags);
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	sched->clk_state = SLIM_CLK_ENTERING_PAUSE;
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	/* clock pause sequence */
928c2ecf20Sopenharmony_ci	ret = slim_do_transfer(ctrl, &txn);
938c2ecf20Sopenharmony_ci	if (ret)
948c2ecf20Sopenharmony_ci		goto clk_pause_ret;
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	txn.mc = SLIM_MSG_MC_NEXT_PAUSE_CLOCK;
978c2ecf20Sopenharmony_ci	txn.rl = 4;
988c2ecf20Sopenharmony_ci	msg.num_bytes = 1;
998c2ecf20Sopenharmony_ci	msg.wbuf = &restart;
1008c2ecf20Sopenharmony_ci	ret = slim_do_transfer(ctrl, &txn);
1018c2ecf20Sopenharmony_ci	if (ret)
1028c2ecf20Sopenharmony_ci		goto clk_pause_ret;
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	txn.mc = SLIM_MSG_MC_RECONFIGURE_NOW;
1058c2ecf20Sopenharmony_ci	txn.rl = 3;
1068c2ecf20Sopenharmony_ci	msg.num_bytes = 1;
1078c2ecf20Sopenharmony_ci	msg.wbuf = NULL;
1088c2ecf20Sopenharmony_ci	ret = slim_do_transfer(ctrl, &txn);
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ciclk_pause_ret:
1118c2ecf20Sopenharmony_ci	if (ret) {
1128c2ecf20Sopenharmony_ci		sched->clk_state = SLIM_CLK_ACTIVE;
1138c2ecf20Sopenharmony_ci	} else {
1148c2ecf20Sopenharmony_ci		sched->clk_state = SLIM_CLK_PAUSED;
1158c2ecf20Sopenharmony_ci		complete(&sched->pause_comp);
1168c2ecf20Sopenharmony_ci	}
1178c2ecf20Sopenharmony_ci	mutex_unlock(&sched->m_reconf);
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	return ret;
1208c2ecf20Sopenharmony_ci}
1218c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(slim_ctrl_clk_pause);
122