Skip to content

Commit 4b14e62

Browse files
Sagar Dhariagregkh
authored andcommitted
slimbus: Add support for 'clock-pause' feature
Per SLIMbus specification, a reconfiguration sequence known as 'clock pause' needs to be broadcast over the bus while entering low- power mode. Clock-pause is initiated by the controller driver. To exit clock-pause, controller typically wakes up the framer device. Since wakeup precedure is controller-specific, framework calls it via controller's function pointer to invoke it. Signed-off-by: Sagar Dharia <[email protected]> Signed-off-by: Srinivas Kandagatla <[email protected]> Reviwed-by: Mark Brown <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent afbdcc7 commit 4b14e62

File tree

5 files changed

+225
-2
lines changed

5 files changed

+225
-2
lines changed

drivers/slimbus/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,4 @@
33
# Makefile for kernel SLIMbus framework.
44
#
55
obj-$(CONFIG_SLIMBUS) += slimbus.o
6-
slimbus-y := core.o messaging.o
6+
slimbus-y := core.o messaging.o sched.o

drivers/slimbus/core.c

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include <linux/init.h>
1010
#include <linux/idr.h>
1111
#include <linux/of.h>
12+
#include <linux/pm_runtime.h>
1213
#include <linux/slimbus.h>
1314
#include "slimbus.h"
1415

@@ -218,6 +219,8 @@ int slim_register_controller(struct slim_controller *ctrl)
218219
ida_init(&ctrl->laddr_ida);
219220
idr_init(&ctrl->tid_idr);
220221
mutex_init(&ctrl->lock);
222+
mutex_init(&ctrl->sched.m_reconf);
223+
init_completion(&ctrl->sched.pause_comp);
221224

222225
dev_dbg(ctrl->dev, "Bus [%s] registered:dev:%p\n",
223226
ctrl->name, ctrl->dev);
@@ -249,6 +252,8 @@ int slim_unregister_controller(struct slim_controller *ctrl)
249252
{
250253
/* Remove all clients */
251254
device_for_each_child(ctrl->dev, NULL, slim_ctrl_remove_device);
255+
/* Enter Clock Pause */
256+
slim_ctrl_clk_pause(ctrl, false, 0);
252257
ida_simple_remove(&ctrl_ida, ctrl->id);
253258

254259
return 0;
@@ -416,6 +421,14 @@ int slim_device_report_present(struct slim_controller *ctrl,
416421
struct slim_device *sbdev;
417422
int ret;
418423

424+
ret = pm_runtime_get_sync(ctrl->dev);
425+
426+
if (ctrl->sched.clk_state != SLIM_CLK_ACTIVE) {
427+
dev_err(ctrl->dev, "slim ctrl not active,state:%d, ret:%d\n",
428+
ctrl->sched.clk_state, ret);
429+
goto slimbus_not_active;
430+
}
431+
419432
sbdev = slim_get_device(ctrl, e_addr);
420433
if (IS_ERR(sbdev))
421434
return -ENODEV;
@@ -427,6 +440,9 @@ int slim_device_report_present(struct slim_controller *ctrl,
427440

428441
ret = slim_device_alloc_laddr(sbdev, true);
429442

443+
slimbus_not_active:
444+
pm_runtime_mark_last_busy(ctrl->dev);
445+
pm_runtime_put_autosuspend(ctrl->dev);
430446
return ret;
431447
}
432448
EXPORT_SYMBOL_GPL(slim_device_report_present);

drivers/slimbus/messaging.c

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
*/
55

66
#include <linux/slab.h>
7+
#include <linux/pm_runtime.h>
78
#include "slimbus.h"
89

910
/**
@@ -46,6 +47,10 @@ void slim_msg_response(struct slim_controller *ctrl, u8 *reply, u8 tid, u8 len)
4647
memcpy(msg->rbuf, reply, len);
4748
if (txn->comp)
4849
complete(txn->comp);
50+
51+
/* Remove runtime-pm vote now that response was received for TID txn */
52+
pm_runtime_mark_last_busy(ctrl->dev);
53+
pm_runtime_put_autosuspend(ctrl->dev);
4954
}
5055
EXPORT_SYMBOL_GPL(slim_msg_response);
5156

@@ -65,10 +70,29 @@ EXPORT_SYMBOL_GPL(slim_msg_response);
6570
int slim_do_transfer(struct slim_controller *ctrl, struct slim_msg_txn *txn)
6671
{
6772
DECLARE_COMPLETION_ONSTACK(done);
68-
bool need_tid;
73+
bool need_tid = false, clk_pause_msg = false;
6974
unsigned long flags;
7075
int ret, tid, timeout;
7176

77+
/*
78+
* do not vote for runtime-PM if the transactions are part of clock
79+
* pause sequence
80+
*/
81+
if (ctrl->sched.clk_state == SLIM_CLK_ENTERING_PAUSE &&
82+
(txn->mt == SLIM_MSG_MT_CORE &&
83+
txn->mc >= SLIM_MSG_MC_BEGIN_RECONFIGURATION &&
84+
txn->mc <= SLIM_MSG_MC_RECONFIGURE_NOW))
85+
clk_pause_msg = true;
86+
87+
if (!clk_pause_msg) {
88+
ret = pm_runtime_get_sync(ctrl->dev);
89+
if (ctrl->sched.clk_state != SLIM_CLK_ACTIVE) {
90+
dev_err(ctrl->dev, "ctrl wrong state:%d, ret:%d\n",
91+
ctrl->sched.clk_state, ret);
92+
goto slim_xfer_err;
93+
}
94+
}
95+
7296
need_tid = slim_tid_txn(txn->mt, txn->mc);
7397

7498
if (need_tid) {
@@ -107,6 +131,15 @@ int slim_do_transfer(struct slim_controller *ctrl, struct slim_msg_txn *txn)
107131
dev_err(ctrl->dev, "Tx:MT:0x%x, MC:0x%x, LA:0x%x failed:%d\n",
108132
txn->mt, txn->mc, txn->la, ret);
109133

134+
slim_xfer_err:
135+
if (!clk_pause_msg && (!need_tid || ret == -ETIMEDOUT)) {
136+
/*
137+
* remove runtime-pm vote if this was TX only, or
138+
* if there was error during this transaction
139+
*/
140+
pm_runtime_mark_last_busy(ctrl->dev);
141+
pm_runtime_mark_last_busy(ctrl->dev);
142+
}
110143
return ret;
111144
}
112145
EXPORT_SYMBOL_GPL(slim_do_transfer);

drivers/slimbus/sched.c

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/*
3+
* Copyright (c) 2011-2017, The Linux Foundation
4+
*/
5+
6+
#include <linux/errno.h>
7+
#include "slimbus.h"
8+
9+
/**
10+
* slim_ctrl_clk_pause() - Called by slimbus controller to enter/exit
11+
* 'clock pause'
12+
* @ctrl: controller requesting bus to be paused or woken up
13+
* @wakeup: Wakeup this controller from clock pause.
14+
* @restart: Restart time value per spec used for clock pause. This value
15+
* 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 (reconfiguration
19+
* 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+
*/
25+
int 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 management
48+
*/
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 wakeup
60+
* Controller should make sure that it sets active framer
61+
* out of clock pause
62+
*/
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+
110+
clk_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+
}
121+
EXPORT_SYMBOL_GPL(slim_ctrl_clk_pause);

drivers/slimbus/slimbus.h

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,16 @@
1414
/* SLIMbus message types. Related to interpretation of message code. */
1515
#define SLIM_MSG_MT_CORE 0x0
1616

17+
/* Clock pause Reconfiguration messages */
18+
#define SLIM_MSG_MC_BEGIN_RECONFIGURATION 0x40
19+
#define SLIM_MSG_MC_NEXT_PAUSE_CLOCK 0x4A
20+
#define SLIM_MSG_MC_RECONFIGURE_NOW 0x5F
21+
22+
/* Clock pause values per SLIMbus spec */
23+
#define SLIM_CLK_FAST 0
24+
#define SLIM_CLK_CONST_PHASE 1
25+
#define SLIM_CLK_UNSPECIFIED 2
26+
1727
/* Destination type Values */
1828
#define SLIM_MSG_DEST_LOGICALADDR 0
1929
#define SLIM_MSG_DEST_ENUMADDR 1
@@ -80,6 +90,42 @@ struct slim_msg_txn {
8090
#define DEFINE_SLIM_LDEST_TXN(name, mc, rl, la, msg) \
8191
struct slim_msg_txn name = { rl, 0, mc, SLIM_MSG_DEST_LOGICALADDR, 0,\
8292
0, la, msg, }
93+
94+
#define DEFINE_SLIM_BCAST_TXN(name, mc, rl, la, msg) \
95+
struct slim_msg_txn name = { rl, 0, mc, SLIM_MSG_DEST_BROADCAST, 0,\
96+
0, la, msg, }
97+
/**
98+
* enum slim_clk_state: SLIMbus controller's clock state used internally for
99+
* maintaining current clock state.
100+
* @SLIM_CLK_ACTIVE: SLIMbus clock is active
101+
* @SLIM_CLK_ENTERING_PAUSE: SLIMbus clock pause sequence is being sent on the
102+
* bus. If this succeeds, state changes to SLIM_CLK_PAUSED. If the
103+
* transition fails, state changes back to SLIM_CLK_ACTIVE
104+
* @SLIM_CLK_PAUSED: SLIMbus controller clock has paused.
105+
*/
106+
enum slim_clk_state {
107+
SLIM_CLK_ACTIVE,
108+
SLIM_CLK_ENTERING_PAUSE,
109+
SLIM_CLK_PAUSED,
110+
};
111+
112+
/**
113+
* struct slim_sched: Framework uses this structure internally for scheduling.
114+
* @clk_state: Controller's clock state from enum slim_clk_state
115+
* @pause_comp: Signals completion of clock pause sequence. This is useful when
116+
* client tries to call SLIMbus transaction when controller is entering
117+
* clock pause.
118+
* @m_reconf: This mutex is held until current reconfiguration (data channel
119+
* scheduling, message bandwidth reservation) is done. Message APIs can
120+
* use the bus concurrently when this mutex is held since elemental access
121+
* messages can be sent on the bus when reconfiguration is in progress.
122+
*/
123+
struct slim_sched {
124+
enum slim_clk_state clk_state;
125+
struct completion pause_comp;
126+
struct mutex m_reconf;
127+
};
128+
83129
/**
84130
* struct slim_controller - Controls every instance of SLIMbus
85131
* (similar to 'master' on SPI)
@@ -95,6 +141,7 @@ struct slim_msg_txn {
95141
* @devices: Slim device list
96142
* @tid_idr: tid id allocator
97143
* @txn_lock: Lock to protect table of transactions
144+
* @sched: scheduler structure used by the controller
98145
* @xfer_msg: Transfer a message on this controller (this can be a broadcast
99146
* control/status message like data channel setup, or a unicast message
100147
* like value element read/write.
@@ -105,6 +152,9 @@ struct slim_msg_txn {
105152
* address table and get_laddr can be used in that case so that controller
106153
* can do this assignment. Use case is when the master is on the remote
107154
* processor side, who is resposible for allocating laddr.
155+
* @wakeup: This function pointer implements controller-specific procedure
156+
* to wake it up from clock-pause. Framework will call this to bring
157+
* the controller out of clock pause.
108158
*
109159
* 'Manager device' is responsible for device management, bandwidth
110160
* allocation, channel setup, and port associations per channel.
@@ -139,12 +189,14 @@ struct slim_controller {
139189
struct list_head devices;
140190
struct idr tid_idr;
141191
spinlock_t txn_lock;
192+
struct slim_sched sched;
142193
int (*xfer_msg)(struct slim_controller *ctrl,
143194
struct slim_msg_txn *tx);
144195
int (*set_laddr)(struct slim_controller *ctrl,
145196
struct slim_eaddr *ea, u8 laddr);
146197
int (*get_laddr)(struct slim_controller *ctrl,
147198
struct slim_eaddr *ea, u8 *laddr);
199+
int (*wakeup)(struct slim_controller *ctrl);
148200
};
149201

150202
int slim_device_report_present(struct slim_controller *ctrl,
@@ -154,6 +206,7 @@ int slim_register_controller(struct slim_controller *ctrl);
154206
int slim_unregister_controller(struct slim_controller *ctrl);
155207
void slim_msg_response(struct slim_controller *ctrl, u8 *reply, u8 tid, u8 l);
156208
int slim_do_transfer(struct slim_controller *ctrl, struct slim_msg_txn *txn);
209+
int slim_ctrl_clk_pause(struct slim_controller *ctrl, bool wakeup, u8 restart);
157210

158211
static inline bool slim_tid_txn(u8 mt, u8 mc)
159212
{

0 commit comments

Comments
 (0)