aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSjur Brændeland <sjur.brandeland@stericsson.com>2012-12-13 23:10:51 -0500
committerRusty Russell <rusty@rustcorp.com.au>2012-12-17 23:50:44 -0500
commit1b6370463e88b0c1c317de16d7b962acc1dab4f2 (patch)
tree778c4e46202565fb8797921d816861c587066f5b
parent276a3e954cfe4da7c492c9063741f99290d2973e (diff)
virtio_console: Add support for remoteproc serial
Add a simple serial connection driver called VIRTIO_ID_RPROC_SERIAL (11) for communicating with a remote processor in an asymmetric multi-processing configuration. This implementation reuses the existing virtio_console implementation, and adds support for DMA allocation of data buffers and disables use of tty console and the virtio control queue. Signed-off-by: Sjur Brændeland <sjur.brandeland@stericsson.com> Acked-by: Amit Shah <amit.shah@redhat.com> Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
-rw-r--r--drivers/char/virtio_console.c192
-rw-r--r--include/uapi/linux/virtio_ids.h1
2 files changed, 170 insertions, 23 deletions
diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
index 548224686963..55a89a4ae42f 100644
--- a/drivers/char/virtio_console.c
+++ b/drivers/char/virtio_console.c
@@ -37,8 +37,12 @@
37#include <linux/wait.h> 37#include <linux/wait.h>
38#include <linux/workqueue.h> 38#include <linux/workqueue.h>
39#include <linux/module.h> 39#include <linux/module.h>
40#include <linux/dma-mapping.h>
41#include <linux/kconfig.h>
40#include "../tty/hvc/hvc_console.h" 42#include "../tty/hvc/hvc_console.h"
41 43
44#define is_rproc_enabled IS_ENABLED(CONFIG_REMOTEPROC)
45
42/* 46/*
43 * This is a global struct for storing common data for all the devices 47 * This is a global struct for storing common data for all the devices
44 * this driver handles. 48 * this driver handles.
@@ -112,6 +116,15 @@ struct port_buffer {
112 /* offset in the buf from which to consume data */ 116 /* offset in the buf from which to consume data */
113 size_t offset; 117 size_t offset;
114 118
119 /* DMA address of buffer */
120 dma_addr_t dma;
121
122 /* Device we got DMA memory from */
123 struct device *dev;
124
125 /* List of pending dma buffers to free */
126 struct list_head list;
127
115 /* If sgpages == 0 then buf is used */ 128 /* If sgpages == 0 then buf is used */
116 unsigned int sgpages; 129 unsigned int sgpages;
117 130
@@ -331,6 +344,11 @@ static bool is_console_port(struct port *port)
331 return false; 344 return false;
332} 345}
333 346
347static bool is_rproc_serial(const struct virtio_device *vdev)
348{
349 return is_rproc_enabled && vdev->id.device == VIRTIO_ID_RPROC_SERIAL;
350}
351
334static inline bool use_multiport(struct ports_device *portdev) 352static inline bool use_multiport(struct ports_device *portdev)
335{ 353{
336 /* 354 /*
@@ -342,11 +360,13 @@ static inline bool use_multiport(struct ports_device *portdev)
342 return portdev->vdev->features[0] & (1 << VIRTIO_CONSOLE_F_MULTIPORT); 360 return portdev->vdev->features[0] & (1 << VIRTIO_CONSOLE_F_MULTIPORT);
343} 361}
344 362
345static void free_buf(struct port_buffer *buf) 363static DEFINE_SPINLOCK(dma_bufs_lock);
364static LIST_HEAD(pending_free_dma_bufs);
365
366static void free_buf(struct port_buffer *buf, bool can_sleep)
346{ 367{
347 unsigned int i; 368 unsigned int i;
348 369
349 kfree(buf->buf);
350 for (i = 0; i < buf->sgpages; i++) { 370 for (i = 0; i < buf->sgpages; i++) {
351 struct page *page = sg_page(&buf->sg[i]); 371 struct page *page = sg_page(&buf->sg[i]);
352 if (!page) 372 if (!page)
@@ -354,14 +374,57 @@ static void free_buf(struct port_buffer *buf)
354 put_page(page); 374 put_page(page);
355 } 375 }
356 376
377 if (!buf->dev) {
378 kfree(buf->buf);
379 } else if (is_rproc_enabled) {
380 unsigned long flags;
381
382 /* dma_free_coherent requires interrupts to be enabled. */
383 if (!can_sleep) {
384 /* queue up dma-buffers to be freed later */
385 spin_lock_irqsave(&dma_bufs_lock, flags);
386 list_add_tail(&buf->list, &pending_free_dma_bufs);
387 spin_unlock_irqrestore(&dma_bufs_lock, flags);
388 return;
389 }
390 dma_free_coherent(buf->dev, buf->size, buf->buf, buf->dma);
391
392 /* Release device refcnt and allow it to be freed */
393 put_device(buf->dev);
394 }
395
357 kfree(buf); 396 kfree(buf);
358} 397}
359 398
399static void reclaim_dma_bufs(void)
400{
401 unsigned long flags;
402 struct port_buffer *buf, *tmp;
403 LIST_HEAD(tmp_list);
404
405 if (list_empty(&pending_free_dma_bufs))
406 return;
407
408 /* Create a copy of the pending_free_dma_bufs while holding the lock */
409 spin_lock_irqsave(&dma_bufs_lock, flags);
410 list_cut_position(&tmp_list, &pending_free_dma_bufs,
411 pending_free_dma_bufs.prev);
412 spin_unlock_irqrestore(&dma_bufs_lock, flags);
413
414 /* Release the dma buffers, without irqs enabled */
415 list_for_each_entry_safe(buf, tmp, &tmp_list, list) {
416 list_del(&buf->list);
417 free_buf(buf, true);
418 }
419}
420
360static struct port_buffer *alloc_buf(struct virtqueue *vq, size_t buf_size, 421static struct port_buffer *alloc_buf(struct virtqueue *vq, size_t buf_size,
361 int pages) 422 int pages)
362{ 423{
363 struct port_buffer *buf; 424 struct port_buffer *buf;
364 425
426 reclaim_dma_bufs();
427
365 /* 428 /*
366 * Allocate buffer and the sg list. The sg list array is allocated 429 * Allocate buffer and the sg list. The sg list array is allocated
367 * directly after the port_buffer struct. 430 * directly after the port_buffer struct.
@@ -373,11 +436,34 @@ static struct port_buffer *alloc_buf(struct virtqueue *vq, size_t buf_size,
373 436
374 buf->sgpages = pages; 437 buf->sgpages = pages;
375 if (pages > 0) { 438 if (pages > 0) {
439 buf->dev = NULL;
376 buf->buf = NULL; 440 buf->buf = NULL;
377 return buf; 441 return buf;
378 } 442 }
379 443
380 buf->buf = kmalloc(buf_size, GFP_KERNEL); 444 if (is_rproc_serial(vq->vdev)) {
445 /*
446 * Allocate DMA memory from ancestor. When a virtio
447 * device is created by remoteproc, the DMA memory is
448 * associated with the grandparent device:
449 * vdev => rproc => platform-dev.
450 * The code here would have been less quirky if
451 * DMA_MEMORY_INCLUDES_CHILDREN had been supported
452 * in dma-coherent.c
453 */
454 if (!vq->vdev->dev.parent || !vq->vdev->dev.parent->parent)
455 goto free_buf;
456 buf->dev = vq->vdev->dev.parent->parent;
457
458 /* Increase device refcnt to avoid freeing it */
459 get_device(buf->dev);
460 buf->buf = dma_alloc_coherent(buf->dev, buf_size, &buf->dma,
461 GFP_KERNEL);
462 } else {
463 buf->dev = NULL;
464 buf->buf = kmalloc(buf_size, GFP_KERNEL);
465 }
466
381 if (!buf->buf) 467 if (!buf->buf)
382 goto free_buf; 468 goto free_buf;
383 buf->len = 0; 469 buf->len = 0;
@@ -446,7 +532,7 @@ static void discard_port_data(struct port *port)
446 port->stats.bytes_discarded += buf->len - buf->offset; 532 port->stats.bytes_discarded += buf->len - buf->offset;
447 if (add_inbuf(port->in_vq, buf) < 0) { 533 if (add_inbuf(port->in_vq, buf) < 0) {
448 err++; 534 err++;
449 free_buf(buf); 535 free_buf(buf, false);
450 } 536 }
451 port->inbuf = NULL; 537 port->inbuf = NULL;
452 buf = get_inbuf(port); 538 buf = get_inbuf(port);
@@ -518,7 +604,7 @@ static void reclaim_consumed_buffers(struct port *port)
518 return; 604 return;
519 } 605 }
520 while ((buf = virtqueue_get_buf(port->out_vq, &len))) { 606 while ((buf = virtqueue_get_buf(port->out_vq, &len))) {
521 free_buf(buf); 607 free_buf(buf, false);
522 port->outvq_full = false; 608 port->outvq_full = false;
523 } 609 }
524} 610}
@@ -765,7 +851,7 @@ static ssize_t port_fops_write(struct file *filp, const char __user *ubuf,
765 goto out; 851 goto out;
766 852
767free_buf: 853free_buf:
768 free_buf(buf);