blob: c90534dac92397883bca86b3138605bf88d2ede3 [file] [log] [blame]
Arun Kumar Neelakantamec352812018-06-11 18:32:20 +05301// SPDX-License-Identifier: GPL-2.0
2/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
3 */
4
5#include <linux/delay.h>
6#include <linux/interrupt.h>
7#include <linux/ipc_router_xprt.h>
8#include <linux/io.h>
9#include <linux/of_device.h>
10#include <linux/module.h>
11#include <linux/platform_device.h>
12#include <linux/types.h>
13#include <linux/skbuff.h>
14#include <linux/sched.h>
Arun Kumar Neelakantam5b37df62018-07-12 17:39:37 +053015#include <microvisor/microvisor.h>
Arun Kumar Neelakantamec352812018-06-11 18:32:20 +053016
17#define MODULE_NAME "ipc_router_fifo_xprt"
18#define XPRT_NAME_LEN 32
19
20#define FIFO_MAGIC_KEY 0x24495043 /* "$IPC" */
21#define FIFO_SIZE 0x4000
22#define FIFO_0_START 0x1000
23#define FIFO_1_START (FIFO_0_START + FIFO_SIZE)
24#define FIFO_MAGIC_IDX 0x0
25#define TAIL_0_IDX 0x1
26#define HEAD_0_IDX 0x2
27#define TAIL_1_IDX 0x3
28#define HEAD_1_IDX 0x4
29
30struct msm_ipc_pipe {
31 __le32 *tail;
32 __le32 *head;
33
34 void *fifo;
35 size_t length;
36};
37
38/**
39 * ipcr_fifo_xprt - IPC Router's FIFO XPRT structure
40 * @xprt: IPC Router XPRT structure to contain XPRT specific info.
41 * @tx_pipe: TX FIFO specific info.
42 * @rx_pipe: RX FIFO specific info.
43 * @fifo_xprt_wq: Workqueue to queue read & other XPRT related works.
44 * @in_pkt: Pointer to any partially read packet.
45 * @read_work: Read Work to perform read operation from SMD.
46 * @sft_close_complete: Variable to indicate completion of SSR handling
47 * by IPC Router.
48 * @xprt_version: IPC Router header version supported by this XPRT.
49 * @driver: Platform drivers register by this XPRT.
50 * @xprt_name: Name of the XPRT to be registered with IPC Router.
51 */
52struct ipcr_fifo_xprt {
53 struct msm_ipc_router_xprt xprt;
54 struct msm_ipc_pipe tx_pipe;
55 struct msm_ipc_pipe rx_pipe;
56 struct workqueue_struct *xprt_wq;
57 struct rr_packet *in_pkt;
58 struct delayed_work read_work;
59 struct completion sft_close_complete;
60 unsigned int xprt_version;
61 struct platform_driver driver;
62 char xprt_name[XPRT_NAME_LEN];
63 void *fifo_base;
64 size_t fifo_size;
65 int tx_fifo_idx;
66 okl4_kcap_t kcap;
67};
68
69static void xprt_read_data(struct work_struct *work);
70static void ipcr_fifo_raise_virq(struct ipcr_fifo_xprt *xprtp);
71
72static size_t fifo_rx_avail(struct msm_ipc_pipe *pipe)
73{
74 u32 head;
75 u32 tail;
76
77 head = le32_to_cpu(*pipe->head);
78 tail = le32_to_cpu(*pipe->tail);
79
80 if (head < tail)
81 return pipe->length - tail + head;
82
83 return head - tail;
84}
85
86static void fifo_rx_peak(struct msm_ipc_pipe *pipe,
87 void *data, unsigned int offset, size_t count)
88{
89 size_t len;
90 u32 tail;
91
92 tail = le32_to_cpu(*pipe->tail);
93 tail += offset;
94 if (tail >= pipe->length)
95 tail -= pipe->length;
96
97 len = min_t(size_t, count, pipe->length - tail);
98 if (len)
99 memcpy_fromio(data, pipe->fifo + tail, len);
100
101 if (len != count)
102 memcpy_fromio(data + len, pipe->fifo, (count - len));
103}
104
105static void fifo_rx_advance(struct msm_ipc_pipe *pipe, size_t count)
106{
107 u32 tail;
108
109 tail = le32_to_cpu(*pipe->tail);
110
111 tail += count;
112 if (tail > pipe->length)
113 tail -= pipe->length;
114
115 *pipe->tail = cpu_to_le32(tail);
116}
117
118static size_t fifo_tx_avail(struct msm_ipc_pipe *pipe)
119{
120 u32 head;
121 u32 tail;
122 u32 avail;
123
124 head = le32_to_cpu(*pipe->head);
125 tail = le32_to_cpu(*pipe->tail);
126
127 if (tail <= head)
128 avail = pipe->length - head + tail;
129 else
130 avail = tail - head;
131
132 return avail;
133}
134
135static void fifo_tx_write(struct msm_ipc_pipe *pipe,
136 const void *data, size_t count)
137{
138 size_t len;
139 u32 head;
140
141 head = le32_to_cpu(*pipe->head);
142
143 len = min_t(size_t, count, pipe->length - head);
144 if (len)
145 memcpy_toio(pipe->fifo + head, data, len);
146
147 if (len != count)
148 memcpy_toio(pipe->fifo, data + len, count - len);
149
150 head += count;
151 if (head >= pipe->length)
152 head -= pipe->length;
153
154 /* Ensure ordering of fifo and head update */
155 wmb();
156
157 *pipe->head = cpu_to_le32(head);
158}
159
160/**
161 * set_xprt_version() - Set IPC Router header version in the transport
162 * @xprt: Reference to the transport structure.
163 * @version: The version to be set in transport.
164 */
165static void set_xprt_version(struct msm_ipc_router_xprt *xprt,
166 unsigned int version)
167{
168 struct ipcr_fifo_xprt *xprtp;
169
170 if (!xprt)
171 return;
172 xprtp = container_of(xprt, struct ipcr_fifo_xprt, xprt);
173 xprtp->xprt_version = version;
174}
175
176static int get_xprt_version(struct msm_ipc_router_xprt *xprt)
177{
178 struct ipcr_fifo_xprt *xprtp;
179
180 if (!xprt)
181 return -EINVAL;
182 xprtp = container_of(xprt, struct ipcr_fifo_xprt, xprt);
183 return (int)xprtp->xprt_version;
184}
185
186static int get_xprt_option(struct msm_ipc_router_xprt *xprt)
187{
188 /* fragmented data is NOT supported */
189 return 0;
190}
191
192static int xprt_close(struct msm_ipc_router_xprt *xprt)
193{
194 return 0;
195}
196
197static void xprt_sft_close_done(struct msm_ipc_router_xprt *xprt)
198{
199 struct ipcr_fifo_xprt *xprtp;
200
201 if (!xprt)
202 return;
203
204 xprtp = container_of(xprt, struct ipcr_fifo_xprt, xprt);
205 complete_all(&xprtp->sft_close_complete);
206}
207
208static int xprt_write(void *data, uint32_t len,
209 struct msm_ipc_router_xprt *xprt)
210{
211 struct rr_packet *pkt = (struct rr_packet *)data;
212 struct sk_buff *skb;
213 struct ipcr_fifo_xprt *xprtp;
214
215 xprtp = container_of(xprt, struct ipcr_fifo_xprt, xprt);
216
217 if (!pkt)
218 return -EINVAL;
219
220 if (!len || pkt->length != len)
221 return -EINVAL;
222
223 /* TODO: FIFO write : check if we can write full packet at one shot */
224 if (skb_queue_len(pkt->pkt_fragment_q) != 1) {
225 pr_err("IPC router core is given fragmented data\n");
226 return -EINVAL;
227 }
228 if (fifo_tx_avail(&xprtp->tx_pipe) < len) {
229 pr_err("No Space in FIFO\n");
230 return -EAGAIN;
231 }
232
233 skb_queue_walk(pkt->pkt_fragment_q, skb) {
234 fifo_tx_write(&xprtp->tx_pipe, skb->data, skb->len);
235 }
236
237 ipcr_fifo_raise_virq(xprtp);
238
239 return len;
240}
241
242static void xprt_read_data(struct work_struct *work)
243{
244 void *data;
245 size_t hdr_len;
246 size_t rx_avail;
247 size_t pkt_len;
248 struct rr_header_v1 hdr;
249 struct sk_buff *ipc_rtr_pkt;
250 struct ipcr_fifo_xprt *xprtp;
251 struct delayed_work *rwork = to_delayed_work(work);
252
253 xprtp = container_of(rwork, struct ipcr_fifo_xprt, read_work);
254
255 hdr_len = sizeof(struct rr_header_v1);
256 while (1) {
257 rx_avail = fifo_rx_avail(&xprtp->rx_pipe);
Arun Kumar Neelakantamb6912512018-07-19 13:51:39 +0530258 if (!rx_avail)
Arun Kumar Neelakantamec352812018-06-11 18:32:20 +0530259 break;
260
261 fifo_rx_peak(&xprtp->rx_pipe, &hdr, 0, hdr_len);
262 pkt_len = ipc_router_peek_pkt_size((char *)&hdr);
263
264 if (pkt_len < 0) {
265 pr_err("%s invalid pkt_len %zu\n", __func__, pkt_len);
266 break;
267 }
268 if (!xprtp->in_pkt) {
269 xprtp->in_pkt = create_pkt(NULL);
270 if (!xprtp->in_pkt)
271 break;
272 }
273 ipc_rtr_pkt = alloc_skb(pkt_len, GFP_KERNEL);
274 if (!ipc_rtr_pkt) {
275 release_pkt(xprtp->in_pkt);
276 xprtp->in_pkt = NULL;
277 break;
278 }
279 data = skb_put(ipc_rtr_pkt, pkt_len);
280 do {
281 rx_avail = fifo_rx_avail(&xprtp->rx_pipe);
282 if (rx_avail >= pkt_len) {
283 fifo_rx_peak(&xprtp->rx_pipe, data, 0, pkt_len);
284 fifo_rx_advance(&xprtp->rx_pipe, pkt_len);
285 break;
286 }
287 pr_debug("%s wait for FULL PKT [avail: len][%zu:%zu]\n",
288 __func__, rx_avail, pkt_len);
289 /* wait for complete packet written into FIFO */
290 msleep(20);
291 } while (1);
292
293 skb_queue_tail(xprtp->in_pkt->pkt_fragment_q, ipc_rtr_pkt);
294 xprtp->in_pkt->length = pkt_len;
295 msm_ipc_router_xprt_notify(&xprtp->xprt,
296 IPC_ROUTER_XPRT_EVENT_DATA,
297 (void *)xprtp->in_pkt);
298 release_pkt(xprtp->in_pkt);
299 xprtp->in_pkt = NULL;
300 }
301}
302
303static void ipcr_fifo_raise_virq(struct ipcr_fifo_xprt *xprtp)
304{
305 okl4_error_t err;
306 unsigned long payload = 0xffff;
307
308 err = _okl4_sys_vinterrupt_raise(xprtp->kcap, payload);
309}
310
311static irqreturn_t ipcr_fifo_virq_handler(int irq, void *dev_id)
312{
313 struct ipcr_fifo_xprt *xprtp = dev_id;
314
315 queue_delayed_work(xprtp->xprt_wq, &xprtp->read_work, 0);
316 return IRQ_HANDLED;
317}
318
319/**
320 * ipcr_fifo_config_init() - init FIFO xprt configs
321 *
322 * @return: 0 on success, standard Linux error codes on error.
323 *
324 * This function is called to initialize the FIFO XPRT pointer with
325 * the FIFO XPRT configurations either from device tree or static arrays.
326 */
327static int ipcr_fifo_config_init(struct ipcr_fifo_xprt *xprtp)
328{
329 __le32 *descs;
330
331 descs = xprtp->fifo_base;
332 descs[FIFO_MAGIC_IDX] = FIFO_MAGIC_KEY;
333
334 if (xprtp->tx_fifo_idx) {
335 xprtp->tx_pipe.tail = &descs[TAIL_0_IDX];
336 xprtp->tx_pipe.head = &descs[HEAD_0_IDX];
337 xprtp->tx_pipe.fifo = xprtp->fifo_base + FIFO_0_START;
338 xprtp->tx_pipe.length = FIFO_SIZE;
339
340 xprtp->rx_pipe.tail = &descs[TAIL_1_IDX];
341 xprtp->rx_pipe.head = &descs[HEAD_1_IDX];
342 xprtp->rx_pipe.fifo = xprtp->fifo_base + FIFO_1_START;
343 xprtp->rx_pipe.length = FIFO_SIZE;
344 } else {
345 xprtp->tx_pipe.tail = &descs[TAIL_1_IDX];
346 xprtp->tx_pipe.head = &descs[HEAD_1_IDX];
347 xprtp->tx_pipe.fifo = xprtp->fifo_base + FIFO_1_START;
348 xprtp->tx_pipe.length = FIFO_SIZE;
349
350 xprtp->rx_pipe.tail = &descs[TAIL_0_IDX];
351 xprtp->rx_pipe.head = &descs[HEAD_0_IDX];
352 xprtp->rx_pipe.fifo = xprtp->fifo_base + FIFO_0_START;
353 xprtp->rx_pipe.length = FIFO_SIZE;
354 }
355
356 /* Reset respective index */
357 *xprtp->tx_pipe.head = 0;
358 *xprtp->rx_pipe.tail = 0;
359
360 xprtp->xprt.link_id = 1;
361 xprtp->xprt_version = 1;
362
363 strlcpy(xprtp->xprt_name, "IPCR_FIFO_XPRT", XPRT_NAME_LEN);
364 xprtp->xprt.name = xprtp->xprt_name;
365
366 xprtp->xprt.set_version = set_xprt_version;
367 xprtp->xprt.get_version = get_xprt_version;
368 xprtp->xprt.get_option = get_xprt_option;
369 xprtp->xprt.read_avail = NULL;
370 xprtp->xprt.read = NULL;
371 xprtp->xprt.write_avail = NULL;
372 xprtp->xprt.write = xprt_write;
373 xprtp->xprt.close = xprt_close;
374 xprtp->xprt.sft_close_done = xprt_sft_close_done;
375 xprtp->xprt.priv = NULL;
376
377 xprtp->in_pkt = NULL;
378 xprtp->xprt_wq = create_singlethread_workqueue(xprtp->xprt_name);
379 if (!xprtp->xprt_wq)
380 return -EFAULT;
381
382 INIT_DELAYED_WORK(&xprtp->read_work, xprt_read_data);
383
384 msm_ipc_router_xprt_notify(&xprtp->xprt,
385 IPC_ROUTER_XPRT_EVENT_OPEN,
386 NULL);
387
388 if (fifo_rx_avail(&xprtp->rx_pipe))
389 queue_delayed_work(xprtp->xprt_wq, &xprtp->read_work, 0);
390
391 return 0;
392}
393
394/**
395 * ipcr_fifo_xprt_probe() - Probe an FIFO xprt
396 *
397 * @pdev: Platform device corresponding to FIFO xprt.
398 *
399 * @return: 0 on success, standard Linux error codes on error.
400 *
401 * This function is called when the underlying device tree driver registers
402 * a platform device, mapped to an FIFO transport.
403 */
404static int ipcr_fifo_xprt_probe(struct platform_device *pdev)
405{
406 int irq;
407 int ret;
408 struct resource *r;
409 struct device *parent;
410 struct ipcr_fifo_xprt *xprtp;
411 struct device_node *ipc_irq_np;
412 struct device_node *ipc_shm_np;
413 struct platform_device *ipc_shm_dev;
414
415 xprtp = devm_kzalloc(&pdev->dev, sizeof(*xprtp), GFP_KERNEL);
416 if (IS_ERR_OR_NULL(xprtp))
417 return -ENOMEM;
418
419 parent = &pdev->dev;
420 ipc_irq_np = parent->of_node;
421
422 irq = platform_get_irq(pdev, 0);
423
424 if (irq >= 0) {
425 ret = devm_request_irq(parent, irq, ipcr_fifo_virq_handler,
426 IRQF_TRIGGER_RISING, dev_name(parent),
427 xprtp);
428 if (ret < 0)
429 return -ENODEV;
430 }
431
432 /* this kcap is required to raise VIRQ */
433 ret = of_property_read_u32(ipc_irq_np, "reg", &xprtp->kcap);
434 if (ret < 0)
435 return -ENODEV;
436
437 ipc_shm_np = of_parse_phandle(ipc_irq_np, "qcom,ipc-shm", 0);
438 if (!ipc_shm_np)
439 return -ENODEV;
440
441 ipc_shm_dev = of_find_device_by_node(ipc_shm_np);
442 if (!ipc_shm_dev)
443 return -ENODEV;
444
445 r = platform_get_resource(ipc_shm_dev, IORESOURCE_MEM, 0);
446 if (!r) {
447 pr_err("%s failed to get shared FIFO\n", __func__);
448 return -ENODEV;
449 }
450
451 xprtp->tx_fifo_idx = of_property_read_bool(ipc_shm_np,
452 "qcom,tx-is-first");
453
454 xprtp->fifo_size = resource_size(r);
455 xprtp->fifo_base = devm_ioremap_nocache(&pdev->dev, r->start,
456 resource_size(r));
457 if (!xprtp->fifo_base) {
458 pr_err("%s ioreamp_nocache() failed\n", __func__);
459 return -ENOMEM;
460 }
461
462 ret = ipcr_fifo_config_init(xprtp);
463 if (ret) {
464 IPC_RTR_ERR("%s init failed ret[%d]\n", __func__, ret);
465 return ret;
466 }
467
468 return 0;
469}
470
471static const struct of_device_id ipcr_fifo_xprt_match_table[] = {
472 { .compatible = "qcom,ipcr-fifo-xprt" },
473 {},
474};
475
476static struct platform_driver ipcr_fifo_xprt_driver = {
477 .probe = ipcr_fifo_xprt_probe,
478 .driver = {
479 .name = MODULE_NAME,
480 .owner = THIS_MODULE,
481 .of_match_table = ipcr_fifo_xprt_match_table,
482 },
483};
484
485static int __init ipcr_fifo_xprt_init(void)
486{
487 int rc;
488
489 rc = platform_driver_register(&ipcr_fifo_xprt_driver);
490 if (rc) {
491 IPC_RTR_ERR("%s: driver register failed %d\n", __func__, rc);
492 return rc;
493 }
494
495 return 0;
496}
497
498module_init(ipcr_fifo_xprt_init);
499MODULE_DESCRIPTION("IPC Router FIFO XPRT");
500MODULE_LICENSE("GPL v2");