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