blob: 031e67648d7c3083301eebd2b74cb8ff63ebe1ed [file] [log] [blame]
Sagar Dhariaafbdcc72017-12-11 23:43:00 +00001// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (c) 2011-2017, The Linux Foundation
4 */
5
6#include <linux/slab.h>
7#include "slimbus.h"
8
9/**
10 * slim_msg_response() - Deliver Message response received from a device to the
11 * framework.
12 *
13 * @ctrl: Controller handle
14 * @reply: Reply received from the device
15 * @len: Length of the reply
16 * @tid: Transaction ID received with which framework can associate reply.
17 *
18 * Called by controller to inform framework about the response received.
19 * This helps in making the API asynchronous, and controller-driver doesn't need
20 * to manage 1 more table other than the one managed by framework mapping TID
21 * with buffers
22 */
23void slim_msg_response(struct slim_controller *ctrl, u8 *reply, u8 tid, u8 len)
24{
25 struct slim_msg_txn *txn;
26 struct slim_val_inf *msg;
27 unsigned long flags;
28
29 spin_lock_irqsave(&ctrl->txn_lock, flags);
30 txn = idr_find(&ctrl->tid_idr, tid);
31 if (txn == NULL) {
32 spin_unlock_irqrestore(&ctrl->txn_lock, flags);
33 return;
34 }
35
36 msg = txn->msg;
37 if (msg == NULL || msg->rbuf == NULL) {
38 dev_err(ctrl->dev, "Got response to invalid TID:%d, len:%d\n",
39 tid, len);
40 return;
41 }
42
43 idr_remove(&ctrl->tid_idr, tid);
44 spin_unlock_irqrestore(&ctrl->txn_lock, flags);
45
46 memcpy(msg->rbuf, reply, len);
47 if (txn->comp)
48 complete(txn->comp);
49}
50EXPORT_SYMBOL_GPL(slim_msg_response);
51
52/**
53 * slim_do_transfer() - Process a SLIMbus-messaging transaction
54 *
55 * @ctrl: Controller handle
56 * @txn: Transaction to be sent over SLIMbus
57 *
58 * Called by controller to transmit messaging transactions not dealing with
59 * Interface/Value elements. (e.g. transmittting a message to assign logical
60 * address to a slave device
61 *
62 * Return: -ETIMEDOUT: If transmission of this message timed out
63 * (e.g. due to bus lines not being clocked or driven by controller)
64 */
65int slim_do_transfer(struct slim_controller *ctrl, struct slim_msg_txn *txn)
66{
67 DECLARE_COMPLETION_ONSTACK(done);
68 bool need_tid;
69 unsigned long flags;
70 int ret, tid, timeout;
71
72 need_tid = slim_tid_txn(txn->mt, txn->mc);
73
74 if (need_tid) {
75 spin_lock_irqsave(&ctrl->txn_lock, flags);
76 tid = idr_alloc(&ctrl->tid_idr, txn, 0,
77 SLIM_MAX_TIDS, GFP_KERNEL);
78 txn->tid = tid;
79
80 if (!txn->msg->comp)
81 txn->comp = &done;
82 else
83 txn->comp = txn->comp;
84
85 spin_unlock_irqrestore(&ctrl->txn_lock, flags);
86
87 if (tid < 0)
88 return tid;
89 }
90
91 ret = ctrl->xfer_msg(ctrl, txn);
92
93 if (ret && need_tid && !txn->msg->comp) {
94 unsigned long ms = txn->rl + HZ;
95
96 timeout = wait_for_completion_timeout(txn->comp,
97 msecs_to_jiffies(ms));
98 if (!timeout) {
99 ret = -ETIMEDOUT;
100 spin_lock_irqsave(&ctrl->txn_lock, flags);
101 idr_remove(&ctrl->tid_idr, tid);
102 spin_unlock_irqrestore(&ctrl->txn_lock, flags);
103 }
104 }
105
106 if (ret)
107 dev_err(ctrl->dev, "Tx:MT:0x%x, MC:0x%x, LA:0x%x failed:%d\n",
108 txn->mt, txn->mc, txn->la, ret);
109
110 return ret;
111}
112EXPORT_SYMBOL_GPL(slim_do_transfer);
113
114static int slim_val_inf_sanity(struct slim_controller *ctrl,
115 struct slim_val_inf *msg, u8 mc)
116{
117 if (!msg || msg->num_bytes > 16 ||
118 (msg->start_offset + msg->num_bytes) > 0xC00)
119 goto reterr;
120 switch (mc) {
121 case SLIM_MSG_MC_REQUEST_VALUE:
122 case SLIM_MSG_MC_REQUEST_INFORMATION:
123 if (msg->rbuf != NULL)
124 return 0;
125 break;
126
127 case SLIM_MSG_MC_CHANGE_VALUE:
128 case SLIM_MSG_MC_CLEAR_INFORMATION:
129 if (msg->wbuf != NULL)
130 return 0;
131 break;
132
133 case SLIM_MSG_MC_REQUEST_CHANGE_VALUE:
134 case SLIM_MSG_MC_REQUEST_CLEAR_INFORMATION:
135 if (msg->rbuf != NULL && msg->wbuf != NULL)
136 return 0;
137 break;
138 }
139reterr:
140 dev_err(ctrl->dev, "Sanity check failed:msg:offset:0x%x, mc:%d\n",
141 msg->start_offset, mc);
142 return -EINVAL;
143}
144
145static u16 slim_slicesize(int code)
146{
147 static const u8 sizetocode[16] = {
148 0, 1, 2, 3, 3, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7
149 };
150
151 clamp(code, 1, (int)ARRAY_SIZE(sizetocode));
152
153 return sizetocode[code - 1];
154}
155
156/**
157 * slim_xfer_msg() - Transfer a value info message on slim device
158 *
159 * @sbdev: slim device to which this msg has to be transfered
160 * @msg: value info message pointer
161 * @mc: message code of the message
162 *
163 * Called by drivers which want to transfer a vlaue or info elements.
164 *
165 * Return: -ETIMEDOUT: If transmission of this message timed out
166 */
167int slim_xfer_msg(struct slim_device *sbdev, struct slim_val_inf *msg,
168 u8 mc)
169{
170 DEFINE_SLIM_LDEST_TXN(txn_stack, mc, 6, sbdev->laddr, msg);
171 struct slim_msg_txn *txn = &txn_stack;
172 struct slim_controller *ctrl = sbdev->ctrl;
173 int ret;
174 u16 sl;
175
176 if (!ctrl)
177 return -EINVAL;
178
179 ret = slim_val_inf_sanity(ctrl, msg, mc);
180 if (ret)
181 return ret;
182
183 sl = slim_slicesize(msg->num_bytes);
184
185 dev_dbg(ctrl->dev, "SB xfer msg:os:%x, len:%d, MC:%x, sl:%x\n",
186 msg->start_offset, msg->num_bytes, mc, sl);
187
188 txn->ec = ((sl | (1 << 3)) | ((msg->start_offset & 0xFFF) << 4));
189
190 switch (mc) {
191 case SLIM_MSG_MC_REQUEST_CHANGE_VALUE:
192 case SLIM_MSG_MC_CHANGE_VALUE:
193 case SLIM_MSG_MC_REQUEST_CLEAR_INFORMATION:
194 case SLIM_MSG_MC_CLEAR_INFORMATION:
195 txn->rl += msg->num_bytes;
196 default:
197 break;
198 }
199
200 if (slim_tid_txn(txn->mt, txn->mc))
201 txn->rl++;
202
203 return slim_do_transfer(ctrl, txn);
204}
205EXPORT_SYMBOL_GPL(slim_xfer_msg);
206
207static void slim_fill_msg(struct slim_val_inf *msg, u32 addr,
208 size_t count, u8 *rbuf, u8 *wbuf)
209{
210 msg->start_offset = addr;
211 msg->num_bytes = count;
212 msg->rbuf = rbuf;
213 msg->wbuf = wbuf;
214}
215
216/**
217 * slim_read() - Read SLIMbus value element
218 *
219 * @sdev: client handle.
220 * @addr: address of value element to read.
221 * @count: number of bytes to read. Maximum bytes allowed are 16.
222 * @val: will return what the value element value was
223 *
224 * Return: -EINVAL for Invalid parameters, -ETIMEDOUT If transmission of
225 * this message timed out (e.g. due to bus lines not being clocked
226 * or driven by controller)
227 */
228int slim_read(struct slim_device *sdev, u32 addr, size_t count, u8 *val)
229{
230 struct slim_val_inf msg;
231
232 slim_fill_msg(&msg, addr, count, val, NULL);
233
234 return slim_xfer_msg(sdev, &msg, SLIM_MSG_MC_REQUEST_VALUE);
235}
236EXPORT_SYMBOL_GPL(slim_read);
237
238/**
239 * slim_readb() - Read byte from SLIMbus value element
240 *
241 * @sdev: client handle.
242 * @addr: address in the value element to read.
243 *
244 * Return: byte value of value element.
245 */
246int slim_readb(struct slim_device *sdev, u32 addr)
247{
248 int ret;
249 u8 buf;
250
251 ret = slim_read(sdev, addr, 1, &buf);
252 if (ret < 0)
253 return ret;
254 else
255 return buf;
256}
257EXPORT_SYMBOL_GPL(slim_readb);
258
259/**
260 * slim_write() - Write SLIMbus value element
261 *
262 * @sdev: client handle.
263 * @addr: address in the value element to write.
264 * @count: number of bytes to write. Maximum bytes allowed are 16.
265 * @val: value to write to value element
266 *
267 * Return: -EINVAL for Invalid parameters, -ETIMEDOUT If transmission of
268 * this message timed out (e.g. due to bus lines not being clocked
269 * or driven by controller)
270 */
271int slim_write(struct slim_device *sdev, u32 addr, size_t count, u8 *val)
272{
273 struct slim_val_inf msg;
274
275 slim_fill_msg(&msg, addr, count, val, NULL);
276
277 return slim_xfer_msg(sdev, &msg, SLIM_MSG_MC_CHANGE_VALUE);
278}
279EXPORT_SYMBOL_GPL(slim_write);
280
281/**
282 * slim_writeb() - Write byte to SLIMbus value element
283 *
284 * @sdev: client handle.
285 * @addr: address of value element to write.
286 * @value: value to write to value element
287 *
288 * Return: -EINVAL for Invalid parameters, -ETIMEDOUT If transmission of
289 * this message timed out (e.g. due to bus lines not being clocked
290 * or driven by controller)
291 *
292 */
293int slim_writeb(struct slim_device *sdev, u32 addr, u8 value)
294{
295 return slim_write(sdev, addr, 1, &value);
296}
297EXPORT_SYMBOL_GPL(slim_writeb);