162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2011-2017, The Linux Foundation
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/errno.h>
762306a36Sopenharmony_ci#include "slimbus.h"
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci/**
1062306a36Sopenharmony_ci * slim_ctrl_clk_pause() - Called by slimbus controller to enter/exit
1162306a36Sopenharmony_ci *			   'clock pause'
1262306a36Sopenharmony_ci * @ctrl: controller requesting bus to be paused or woken up
1362306a36Sopenharmony_ci * @wakeup: Wakeup this controller from clock pause.
1462306a36Sopenharmony_ci * @restart: Restart time value per spec used for clock pause. This value
1562306a36Sopenharmony_ci *	isn't used when controller is to be woken up.
1662306a36Sopenharmony_ci *
1762306a36Sopenharmony_ci * Slimbus specification needs this sequence to turn-off clocks for the bus.
1862306a36Sopenharmony_ci * The sequence involves sending 3 broadcast messages (reconfiguration
1962306a36Sopenharmony_ci * sequence) to inform all devices on the bus.
2062306a36Sopenharmony_ci * To exit clock-pause, controller typically wakes up active framer device.
2162306a36Sopenharmony_ci * This API executes clock pause reconfiguration sequence if wakeup is false.
2262306a36Sopenharmony_ci * If wakeup is true, controller's wakeup is called.
2362306a36Sopenharmony_ci * For entering clock-pause, -EBUSY is returned if a message txn in pending.
2462306a36Sopenharmony_ci */
2562306a36Sopenharmony_ciint slim_ctrl_clk_pause(struct slim_controller *ctrl, bool wakeup, u8 restart)
2662306a36Sopenharmony_ci{
2762306a36Sopenharmony_ci	int i, ret = 0;
2862306a36Sopenharmony_ci	unsigned long flags;
2962306a36Sopenharmony_ci	struct slim_sched *sched = &ctrl->sched;
3062306a36Sopenharmony_ci	struct slim_val_inf msg = {0, 0, NULL, NULL};
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci	DEFINE_SLIM_BCAST_TXN(txn, SLIM_MSG_MC_BEGIN_RECONFIGURATION,
3362306a36Sopenharmony_ci				3, SLIM_LA_MANAGER, &msg);
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci	if (wakeup == false && restart > SLIM_CLK_UNSPECIFIED)
3662306a36Sopenharmony_ci		return -EINVAL;
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	mutex_lock(&sched->m_reconf);
3962306a36Sopenharmony_ci	if (wakeup) {
4062306a36Sopenharmony_ci		if (sched->clk_state == SLIM_CLK_ACTIVE) {
4162306a36Sopenharmony_ci			mutex_unlock(&sched->m_reconf);
4262306a36Sopenharmony_ci			return 0;
4362306a36Sopenharmony_ci		}
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci		/*
4662306a36Sopenharmony_ci		 * Fine-tune calculation based on clock gear,
4762306a36Sopenharmony_ci		 * message-bandwidth after bandwidth management
4862306a36Sopenharmony_ci		 */
4962306a36Sopenharmony_ci		ret = wait_for_completion_timeout(&sched->pause_comp,
5062306a36Sopenharmony_ci				msecs_to_jiffies(100));
5162306a36Sopenharmony_ci		if (!ret) {
5262306a36Sopenharmony_ci			mutex_unlock(&sched->m_reconf);
5362306a36Sopenharmony_ci			pr_err("Previous clock pause did not finish");
5462306a36Sopenharmony_ci			return -ETIMEDOUT;
5562306a36Sopenharmony_ci		}
5662306a36Sopenharmony_ci		ret = 0;
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci		/*
5962306a36Sopenharmony_ci		 * Slimbus framework will call controller wakeup
6062306a36Sopenharmony_ci		 * Controller should make sure that it sets active framer
6162306a36Sopenharmony_ci		 * out of clock pause
6262306a36Sopenharmony_ci		 */
6362306a36Sopenharmony_ci		if (sched->clk_state == SLIM_CLK_PAUSED && ctrl->wakeup)
6462306a36Sopenharmony_ci			ret = ctrl->wakeup(ctrl);
6562306a36Sopenharmony_ci		if (!ret)
6662306a36Sopenharmony_ci			sched->clk_state = SLIM_CLK_ACTIVE;
6762306a36Sopenharmony_ci		mutex_unlock(&sched->m_reconf);
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci		return ret;
7062306a36Sopenharmony_ci	}
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	/* already paused */
7362306a36Sopenharmony_ci	if (ctrl->sched.clk_state == SLIM_CLK_PAUSED) {
7462306a36Sopenharmony_ci		mutex_unlock(&sched->m_reconf);
7562306a36Sopenharmony_ci		return 0;
7662306a36Sopenharmony_ci	}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	spin_lock_irqsave(&ctrl->txn_lock, flags);
7962306a36Sopenharmony_ci	for (i = 0; i < SLIM_MAX_TIDS; i++) {
8062306a36Sopenharmony_ci		/* Pending response for a message */
8162306a36Sopenharmony_ci		if (idr_find(&ctrl->tid_idr, i)) {
8262306a36Sopenharmony_ci			spin_unlock_irqrestore(&ctrl->txn_lock, flags);
8362306a36Sopenharmony_ci			mutex_unlock(&sched->m_reconf);
8462306a36Sopenharmony_ci			return -EBUSY;
8562306a36Sopenharmony_ci		}
8662306a36Sopenharmony_ci	}
8762306a36Sopenharmony_ci	spin_unlock_irqrestore(&ctrl->txn_lock, flags);
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	sched->clk_state = SLIM_CLK_ENTERING_PAUSE;
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	/* clock pause sequence */
9262306a36Sopenharmony_ci	ret = slim_do_transfer(ctrl, &txn);
9362306a36Sopenharmony_ci	if (ret)
9462306a36Sopenharmony_ci		goto clk_pause_ret;
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	txn.mc = SLIM_MSG_MC_NEXT_PAUSE_CLOCK;
9762306a36Sopenharmony_ci	txn.rl = 4;
9862306a36Sopenharmony_ci	msg.num_bytes = 1;
9962306a36Sopenharmony_ci	msg.wbuf = &restart;
10062306a36Sopenharmony_ci	ret = slim_do_transfer(ctrl, &txn);
10162306a36Sopenharmony_ci	if (ret)
10262306a36Sopenharmony_ci		goto clk_pause_ret;
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	txn.mc = SLIM_MSG_MC_RECONFIGURE_NOW;
10562306a36Sopenharmony_ci	txn.rl = 3;
10662306a36Sopenharmony_ci	msg.num_bytes = 1;
10762306a36Sopenharmony_ci	msg.wbuf = NULL;
10862306a36Sopenharmony_ci	ret = slim_do_transfer(ctrl, &txn);
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ciclk_pause_ret:
11162306a36Sopenharmony_ci	if (ret) {
11262306a36Sopenharmony_ci		sched->clk_state = SLIM_CLK_ACTIVE;
11362306a36Sopenharmony_ci	} else {
11462306a36Sopenharmony_ci		sched->clk_state = SLIM_CLK_PAUSED;
11562306a36Sopenharmony_ci		complete(&sched->pause_comp);
11662306a36Sopenharmony_ci	}
11762306a36Sopenharmony_ci	mutex_unlock(&sched->m_reconf);
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	return ret;
12062306a36Sopenharmony_ci}
12162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(slim_ctrl_clk_pause);
122