Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Copyright (c) 2011-2017, The Linux Foundation
0004  */
0005 
0006 #include <linux/errno.h>
0007 #include "slimbus.h"
0008 
0009 /**
0010  * slim_ctrl_clk_pause() - Called by slimbus controller to enter/exit
0011  *             'clock pause'
0012  * @ctrl: controller requesting bus to be paused or woken up
0013  * @wakeup: Wakeup this controller from clock pause.
0014  * @restart: Restart time value per spec used for clock pause. This value
0015  *  isn't used when controller is to be woken up.
0016  *
0017  * Slimbus specification needs this sequence to turn-off clocks for the bus.
0018  * The sequence involves sending 3 broadcast messages (reconfiguration
0019  * sequence) to inform all devices on the bus.
0020  * To exit clock-pause, controller typically wakes up active framer device.
0021  * This API executes clock pause reconfiguration sequence if wakeup is false.
0022  * If wakeup is true, controller's wakeup is called.
0023  * For entering clock-pause, -EBUSY is returned if a message txn in pending.
0024  */
0025 int slim_ctrl_clk_pause(struct slim_controller *ctrl, bool wakeup, u8 restart)
0026 {
0027     int i, ret = 0;
0028     unsigned long flags;
0029     struct slim_sched *sched = &ctrl->sched;
0030     struct slim_val_inf msg = {0, 0, NULL, NULL};
0031 
0032     DEFINE_SLIM_BCAST_TXN(txn, SLIM_MSG_MC_BEGIN_RECONFIGURATION,
0033                 3, SLIM_LA_MANAGER, &msg);
0034 
0035     if (wakeup == false && restart > SLIM_CLK_UNSPECIFIED)
0036         return -EINVAL;
0037 
0038     mutex_lock(&sched->m_reconf);
0039     if (wakeup) {
0040         if (sched->clk_state == SLIM_CLK_ACTIVE) {
0041             mutex_unlock(&sched->m_reconf);
0042             return 0;
0043         }
0044 
0045         /*
0046          * Fine-tune calculation based on clock gear,
0047          * message-bandwidth after bandwidth management
0048          */
0049         ret = wait_for_completion_timeout(&sched->pause_comp,
0050                 msecs_to_jiffies(100));
0051         if (!ret) {
0052             mutex_unlock(&sched->m_reconf);
0053             pr_err("Previous clock pause did not finish");
0054             return -ETIMEDOUT;
0055         }
0056         ret = 0;
0057 
0058         /*
0059          * Slimbus framework will call controller wakeup
0060          * Controller should make sure that it sets active framer
0061          * out of clock pause
0062          */
0063         if (sched->clk_state == SLIM_CLK_PAUSED && ctrl->wakeup)
0064             ret = ctrl->wakeup(ctrl);
0065         if (!ret)
0066             sched->clk_state = SLIM_CLK_ACTIVE;
0067         mutex_unlock(&sched->m_reconf);
0068 
0069         return ret;
0070     }
0071 
0072     /* already paused */
0073     if (ctrl->sched.clk_state == SLIM_CLK_PAUSED) {
0074         mutex_unlock(&sched->m_reconf);
0075         return 0;
0076     }
0077 
0078     spin_lock_irqsave(&ctrl->txn_lock, flags);
0079     for (i = 0; i < SLIM_MAX_TIDS; i++) {
0080         /* Pending response for a message */
0081         if (idr_find(&ctrl->tid_idr, i)) {
0082             spin_unlock_irqrestore(&ctrl->txn_lock, flags);
0083             mutex_unlock(&sched->m_reconf);
0084             return -EBUSY;
0085         }
0086     }
0087     spin_unlock_irqrestore(&ctrl->txn_lock, flags);
0088 
0089     sched->clk_state = SLIM_CLK_ENTERING_PAUSE;
0090 
0091     /* clock pause sequence */
0092     ret = slim_do_transfer(ctrl, &txn);
0093     if (ret)
0094         goto clk_pause_ret;
0095 
0096     txn.mc = SLIM_MSG_MC_NEXT_PAUSE_CLOCK;
0097     txn.rl = 4;
0098     msg.num_bytes = 1;
0099     msg.wbuf = &restart;
0100     ret = slim_do_transfer(ctrl, &txn);
0101     if (ret)
0102         goto clk_pause_ret;
0103 
0104     txn.mc = SLIM_MSG_MC_RECONFIGURE_NOW;
0105     txn.rl = 3;
0106     msg.num_bytes = 1;
0107     msg.wbuf = NULL;
0108     ret = slim_do_transfer(ctrl, &txn);
0109 
0110 clk_pause_ret:
0111     if (ret) {
0112         sched->clk_state = SLIM_CLK_ACTIVE;
0113     } else {
0114         sched->clk_state = SLIM_CLK_PAUSED;
0115         complete(&sched->pause_comp);
0116     }
0117     mutex_unlock(&sched->m_reconf);
0118 
0119     return ret;
0120 }
0121 EXPORT_SYMBOL_GPL(slim_ctrl_clk_pause);