aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>2012-12-05 14:29:48 -0800
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2012-12-05 14:29:48 -0800
commitc8c7af548d05efd37a5a7d14e2019317bca9bdf8 (patch)
treee2a32f555653ea967fbcef67ceac35209fe20374
parent8f2828f9846021088223c43284d12b8ba0eac0d0 (diff)
downloadltsi-kernel-c8c7af548d05efd37a5a7d14e2019317bca9bdf8.tar.gz
UIO driver patches added
-rw-r--r--patches.uio/drivers-uio-add-new-uio-device-for-dynamic-memory-allocation.patch469
-rw-r--r--patches.uio/drivers-uio-add-uio_dmem_genirq-description-to-uio-documentation.patch84
-rw-r--r--patches.uio/drivers-uio-only-allocate-new-private-data-when-probing-device-tree-node.patch50
-rw-r--r--patches.uio/drivers-uio_dmem_genirq-allow-partial-success-when-opening-device.patch56
-rw-r--r--patches.uio/drivers-uio_dmem_genirq-don-t-mix-address-spaces-for-dynamic-region-vaddr.patch80
-rw-r--r--patches.uio/drivers-uio_dmem_genirq-don-t-use-dma_error_code-to-indicate-unmapped-regions.patch75
-rw-r--r--series10
7 files changed, 824 insertions, 0 deletions
diff --git a/patches.uio/drivers-uio-add-new-uio-device-for-dynamic-memory-allocation.patch b/patches.uio/drivers-uio-add-new-uio-device-for-dynamic-memory-allocation.patch
new file mode 100644
index 0000000000000..1fd4f908d6f2a
--- /dev/null
+++ b/patches.uio/drivers-uio-add-new-uio-device-for-dynamic-memory-allocation.patch
@@ -0,0 +1,469 @@
+From dhobsong@igel.co.jp Wed Nov 21 18:27:13 2012
+From: Damian Hobson-Garcia <dhobsong@igel.co.jp>
+Date: Thu, 22 Nov 2012 11:26:48 +0900
+Subject: [PATCH 1/6] drivers: uio: Add new uio device for dynamic memory allocation
+To: gregkh@linuxfoundation.org
+Cc: ltsi-dev@lists.linuxfoundation.org, Damian Hobson-Garcia <dhobsong@igel.co.jp>
+Message-ID: <1353551213-17996-2-git-send-email-dhobsong@igel.co.jp>
+
+
+This device extends the uio_pdrv_genirq driver to provide limited
+dynamic memory allocation for UIO devices. This allows UIO devices
+to use CMA and IOMMU allocated memory regions. This driver is based
+on the uio_pdrv_genirq driver and provides the same generic interrupt
+handling capabilities. Like uio_prdv_genirq,
+a fixed number of memory regions, defined in the platform device's
+.resources field are exported to userpace. This driver adds the ability
+to export additional regions whose number and size are known at boot time,
+but whose memory is not allocated until the uio device file is opened for
+the first time. When the device file is closed, the allocated memory block
+is freed. Physical (DMA) addresses for the dynamic regions are provided to
+the userspace via /sys/class/uio/uioX/maps/mapY/addr in the same way as
+static addresses are when the uio device file is open, when no processes
+are holding the device file open, the address returned to userspace is
+DMA_ERROR_CODE.
+
+Signed-off-by: Damian Hobson-Garcia <dhobsong@igel.co.jp>
+---
+ drivers/uio/Kconfig | 16 ++
+ drivers/uio/Makefile | 1 +
+ drivers/uio/uio_dmem_genirq.c | 354 +++++++++++++++++++++++++
+ include/linux/platform_data/uio_dmem_genirq.h | 26 ++
+ 4 files changed, 397 insertions(+), 0 deletions(-)
+ create mode 100644 drivers/uio/uio_dmem_genirq.c
+ create mode 100644 include/linux/platform_data/uio_dmem_genirq.h
+
+diff --git a/drivers/uio/Kconfig b/drivers/uio/Kconfig
+index 6f3ea9b..82e2b89 100644
+--- a/drivers/uio/Kconfig
++++ b/drivers/uio/Kconfig
+@@ -44,6 +44,22 @@ config UIO_PDRV_GENIRQ
+
+ If you don't know what to do here, say N.
+
++config UIO_DMEM_GENIRQ
++ tristate "Userspace platform driver with generic irq and dynamic memory"
++ help
++ Platform driver for Userspace I/O devices, including generic
++ interrupt handling code. Shared interrupts are not supported.
++
++ Memory regions can be specified with the same platform device
++ resources as the UIO_PDRV drivers, but dynamic regions can also
++ be specified.
++ The number and size of these regions is static,
++ but the memory allocation is not performed until
++ the associated device file is opened. The
++ memory is freed once the uio device is closed.
++
++ If you don't know what to do here, say N.
++
+ config UIO_AEC
+ tristate "AEC video timestamp device"
+ depends on PCI
+diff --git a/drivers/uio/Makefile b/drivers/uio/Makefile
+index d4dd9a5..b354c53 100644
+--- a/drivers/uio/Makefile
++++ b/drivers/uio/Makefile
+@@ -2,6 +2,7 @@ obj-$(CONFIG_UIO) += uio.o
+ obj-$(CONFIG_UIO_CIF) += uio_cif.o
+ obj-$(CONFIG_UIO_PDRV) += uio_pdrv.o
+ obj-$(CONFIG_UIO_PDRV_GENIRQ) += uio_pdrv_genirq.o
++obj-$(CONFIG_UIO_DMEM_GENIRQ) += uio_dmem_genirq.o
+ obj-$(CONFIG_UIO_AEC) += uio_aec.o
+ obj-$(CONFIG_UIO_SERCOS3) += uio_sercos3.o
+ obj-$(CONFIG_UIO_PCI_GENERIC) += uio_pci_generic.o
+diff --git a/drivers/uio/uio_dmem_genirq.c b/drivers/uio/uio_dmem_genirq.c
+new file mode 100644
+index 0000000..4d4dd00
+--- /dev/null
++++ b/drivers/uio/uio_dmem_genirq.c
+@@ -0,0 +1,354 @@
++/*
++ * drivers/uio/uio_dmem_genirq.c
++ *
++ * Userspace I/O platform driver with generic IRQ handling code.
++ *
++ * Copyright (C) 2012 Damian Hobson-Garcia
++ *
++ * Based on uio_pdrv_genirq.c by Magnus Damm
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License version 2 as published by
++ * the Free Software Foundation.
++ */
++
++#include <linux/platform_device.h>
++#include <linux/uio_driver.h>
++#include <linux/spinlock.h>
++#include <linux/bitops.h>
++#include <linux/module.h>
++#include <linux/interrupt.h>
++#include <linux/platform_data/uio_dmem_genirq.h>
++#include <linux/stringify.h>
++#include <linux/pm_runtime.h>
++#include <linux/dma-mapping.h>
++#include <linux/slab.h>
++
++#include <linux/of.h>
++#include <linux/of_platform.h>
++#include <linux/of_address.h>
++
++#define DRIVER_NAME "uio_dmem_genirq"
++
++struct uio_dmem_genirq_platdata {
++ struct uio_info *uioinfo;
++ spinlock_t lock;
++ unsigned long flags;
++ struct platform_device *pdev;
++ unsigned int dmem_region_start;
++ unsigned int num_dmem_regions;
++ struct mutex alloc_lock;
++ unsigned int refcnt;
++};
++
++static int uio_dmem_genirq_open(struct uio_info *info, struct inode *inode)
++{
++ struct uio_dmem_genirq_platdata *priv = info->priv;
++ struct uio_mem *uiomem;
++ int ret = 0;
++
++ uiomem = &priv->uioinfo->mem[priv->dmem_region_start];
++
++ mutex_lock(&priv->alloc_lock);
++ while (!priv->refcnt && uiomem < &priv->uioinfo->mem[MAX_UIO_MAPS]) {
++ void *addr;
++ if (!uiomem->size)
++ break;
++
++ addr = dma_alloc_coherent(&priv->pdev->dev, uiomem->size,
++ (dma_addr_t *)&uiomem->addr, GFP_KERNEL);
++ if (!addr) {
++ ret = -ENOMEM;
++ break;
++ }
++
++ uiomem->internal_addr = addr;
++ ++uiomem;
++ }
++ priv->refcnt++;
++
++ mutex_unlock(&priv->alloc_lock);
++ /* Wait until the Runtime PM code has woken up the device */
++ pm_runtime_get_sync(&priv->pdev->dev);
++ return ret;
++}
++
++static int uio_dmem_genirq_release(struct uio_info *info, struct inode *inode)
++{
++ struct uio_dmem_genirq_platdata *priv = info->priv;
++ struct uio_mem *uiomem;
++
++ /* Tell the Runtime PM code that the device has become idle */
++ pm_runtime_put_sync(&priv->pdev->dev);
++
++ uiomem = &priv->uioinfo->mem[priv->dmem_region_start];
++
++ mutex_lock(&priv->alloc_lock);
++
++ priv->refcnt--;
++ while (!priv->refcnt && uiomem < &priv->uioinfo->mem[MAX_UIO_MAPS]) {
++ if (!uiomem->size)
++ break;
++
++ dma_free_coherent(&priv->pdev->dev, uiomem->size,
++ uiomem->internal_addr, uiomem->addr);
++ uiomem->addr = DMA_ERROR_CODE;
++ ++uiomem;
++ }
++
++ mutex_unlock(&priv->alloc_lock);
++ return 0;
++}
++
++static irqreturn_t uio_dmem_genirq_handler(int irq, struct uio_info *dev_info)
++{
++ struct uio_dmem_genirq_platdata *priv = dev_info->priv;
++
++ /* Just disable the interrupt in the interrupt controller, and
++ * remember the state so we can allow user space to enable it later.
++ */
++
++ if (!test_and_set_bit(0, &priv->flags))
++ disable_irq_nosync(irq);
++
++ return IRQ_HANDLED;
++}
++
++static int uio_dmem_genirq_irqcontrol(struct uio_info *dev_info, s32 irq_on)
++{
++ struct uio_dmem_genirq_platdata *priv = dev_info->priv;
++ unsigned long flags;
++
++ /* Allow user space to enable and disable the interrupt
++ * in the interrupt controller, but keep track of the
++ * state to prevent per-irq depth damage.
++ *
++ * Serialize this operation to support multiple tasks.
++ */
++
++ spin_lock_irqsave(&priv->lock, flags);
++ if (irq_on) {
++ if (test_and_clear_bit(0, &priv->flags))
++ enable_irq(dev_info->irq);
++ } else {
++ if (!test_and_set_bit(0, &priv->flags))
++ disable_irq(dev_info->irq);
++ }
++ spin_unlock_irqrestore(&priv->lock, flags);
++
++ return 0;
++}
++
++static int uio_dmem_genirq_probe(struct platform_device *pdev)
++{
++ struct uio_dmem_genirq_pdata *pdata = pdev->dev.platform_data;
++ struct uio_info *uioinfo = &pdata->uioinfo;
++ struct uio_dmem_genirq_platdata *priv;
++ struct uio_mem *uiomem;
++ int ret = -EINVAL;
++ int i;
++
++ if (!uioinfo) {
++ int irq;
++
++ /* alloc uioinfo for one device */
++ uioinfo = kzalloc(sizeof(*uioinfo), GFP_KERNEL);
++ if (!uioinfo) {
++ ret = -ENOMEM;
++ dev_err(&pdev->dev, "unable to kmalloc\n");
++ goto bad2;
++ }
++ uioinfo->name = pdev->dev.of_node->name;
++ uioinfo->version = "devicetree";
++
++ /* Multiple IRQs are not supported */
++ irq = platform_get_irq(pdev, 0);
++ if (irq == -ENXIO)
++ uioinfo->irq = UIO_IRQ_NONE;
++ else
++ uioinfo->irq = irq;
++ }
++
++ if (!uioinfo || !uioinfo->name || !uioinfo->version) {
++ dev_err(&pdev->dev, "missing platform_data\n");
++ goto bad0;
++ }
++
++ if (uioinfo->handler || uioinfo->irqcontrol ||
++ uioinfo->irq_flags & IRQF_SHARED) {
++ dev_err(&pdev->dev, "interrupt configuration error\n");
++ goto bad0;
++ }
++
++ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
++ if (!priv) {
++ ret = -ENOMEM;
++ dev_err(&pdev->dev, "unable to kmalloc\n");
++ goto bad0;
++ }
++
++ dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));
++
++ priv->uioinfo = uioinfo;
++ spin_lock_init(&priv->lock);
++ priv->flags = 0; /* interrupt is enabled to begin with */
++ priv->pdev = pdev;
++ mutex_init(&priv->alloc_lock);
++
++ if (!uioinfo->irq) {
++ ret = platform_get_irq(pdev, 0);
++ if (ret < 0) {
++ dev_err(&pdev->dev, "failed to get IRQ\n");
++ goto bad0;
++ }
++ uioinfo->irq = ret;
++ }
++ uiomem = &uioinfo->mem[0];
++
++ for (i = 0; i < pdev->num_resources; ++i) {
++ struct resource *r = &pdev->resource[i];
++
++ if (r->flags != IORESOURCE_MEM)
++ continue;
++
++ if (uiomem >= &uioinfo->mem[MAX_UIO_MAPS]) {
++ dev_warn(&pdev->dev, "device has more than "
++ __stringify(MAX_UIO_MAPS)
++ " I/O memory resources.\n");
++ break;
++ }
++
++ uiomem->memtype = UIO_MEM_PHYS;
++ uiomem->addr = r->start;
++ uiomem->size = resource_size(r);
++ ++uiomem;
++ }
++
++ priv->dmem_region_start = i;
++ priv->num_dmem_regions = pdata->num_dynamic_regions;
++
++ for (i = 0; i < pdata->num_dynamic_regions; ++i) {
++ if (uiomem >= &uioinfo->mem[MAX_UIO_MAPS]) {
++ dev_warn(&pdev->dev, "device has more than "
++ __stringify(MAX_UIO_MAPS)
++ " dynamic and fixed memory regions.\n");
++ break;
++ }
++ uiomem->memtype = UIO_MEM_PHYS;
++ uiomem->addr = DMA_ERROR_CODE;
++ uiomem->size = pdata->dynamic_region_sizes[i];
++ ++uiomem;
++ }
++
++ while (uiomem < &uioinfo->mem[MAX_UIO_MAPS]) {
++ uiomem->size = 0;
++ ++uiomem;
++ }
++
++ /* This driver requires no hardware specific kernel code to handle
++ * interrupts. Instead, the interrupt handler simply disables the
++ * interrupt in the interrupt controller. User space is responsible
++ * for performing hardware specific acknowledge and re-enabling of
++ * the interrupt in the interrupt controller.
++ *
++ * Interrupt sharing is not supported.
++ */
++
++ uioinfo->handler = uio_dmem_genirq_handler;
++ uioinfo->irqcontrol = uio_dmem_genirq_irqcontrol;
++ uioinfo->open = uio_dmem_genirq_open;
++ uioinfo->release = uio_dmem_genirq_release;
++ uioinfo->priv = priv;
++
++ /* Enable Runtime PM for this device:
++ * The device starts in suspended state to allow the hardware to be
++ * turned off by default. The Runtime PM bus code should power on the
++ * hardware and enable clocks at open().
++ */
++ pm_runtime_enable(&pdev->dev);
++
++ ret = uio_register_device(&pdev->dev, priv->uioinfo);
++ if (ret) {
++ dev_err(&pdev->dev, "unable to register uio device\n");
++ goto bad1;
++ }
++
++ platform_set_drvdata(pdev, priv);
++ return 0;
++ bad1:
++ kfree(priv);
++ pm_runtime_disable(&pdev->dev);
++ bad0:
++ /* kfree uioinfo for OF */
++ if (pdev->dev.of_node)
++ kfree(uioinfo);
++ bad2:
++ return ret;
++}
++
++static int uio_dmem_genirq_remove(struct platform_device *pdev)
++{
++ struct uio_dmem_genirq_platdata *priv = platform_get_drvdata(pdev);
++
++ uio_unregister_device(priv->uioinfo);
++ pm_runtime_disable(&pdev->dev);
++
++ priv->uioinfo->handler = NULL;
++ priv->uioinfo->irqcontrol = NULL;
++
++ /* kfree uioinfo for OF */
++ if (pdev->dev.of_node)
++ kfree(priv->uioinfo);
++
++ kfree(priv);
++ return 0;
++}
++
++static int uio_dmem_genirq_runtime_nop(struct device *dev)
++{
++ /* Runtime PM callback shared between ->runtime_suspend()
++ * and ->runtime_resume(). Simply returns success.
++ *
++ * In this driver pm_runtime_get_sync() and pm_runtime_put_sync()
++ * are used at open() and release() time. This allows the
++ * Runtime PM code to turn off power to the device while the
++ * device is unused, ie before open() and after release().
++ *
++ * This Runtime PM callback does not need to save or restore
++ * any registers since user space is responsbile for hardware
++ * register reinitialization after open().
++ */
++ return 0;
++}
++
++static const struct dev_pm_ops uio_dmem_genirq_dev_pm_ops = {
++ .runtime_suspend = uio_dmem_genirq_runtime_nop,
++ .runtime_resume = uio_dmem_genirq_runtime_nop,
++};
++
++#ifdef CONFIG_OF
++static const struct of_device_id uio_of_genirq_match[] = {
++ { /* empty for now */ },
++};
++MODULE_DEVICE_TABLE(of, uio_of_genirq_match);
++#else
++# define uio_of_genirq_match NULL
++#endif
++
++static struct platform_driver uio_dmem_genirq = {
++ .probe = uio_dmem_genirq_probe,
++ .remove = uio_dmem_genirq_remove,
++ .driver = {
++ .name = DRIVER_NAME,
++ .owner = THIS_MODULE,
++ .pm = &uio_dmem_genirq_dev_pm_ops,
++ .of_match_table = uio_of_genirq_match,
++ },
++};
++
++module_platform_driver(uio_dmem_genirq);
++
++MODULE_AUTHOR("Damian Hobson-Garcia");
++MODULE_DESCRIPTION("Userspace I/O platform driver with dynamic memory.");
++MODULE_LICENSE("GPL v2");
++MODULE_ALIAS("platform:" DRIVER_NAME);
+diff --git a/include/linux/platform_data/uio_dmem_genirq.h b/include/linux/platform_data/uio_dmem_genirq.h
+new file mode 100644
+index 0000000..973c1bb
+--- /dev/null
++++ b/include/linux/platform_data/uio_dmem_genirq.h
+@@ -0,0 +1,26 @@
++/*
++ * include/linux/platform_data/uio_dmem_genirq.h
++ *
++ * Copyright (C) 2012 Damian Hobson-Garcia
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation version 2.
++ *
++ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
++ * kind, whether express or implied; without even the implied warranty
++ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ */
++
++#ifndef _UIO_DMEM_GENIRQ_H
++#define _UIO_DMEM_GENIRQ_H
++
++#include <linux/uio_driver.h>
++
++struct uio_dmem_genirq_pdata {
++ struct uio_info uioinfo;
++ unsigned int *dynamic_region_sizes;
++ unsigned int num_dynamic_regions;
++};
++#endif /* _UIO_DMEM_GENIRQ_H */
+--
+1.7.5.4
+
diff --git a/patches.uio/drivers-uio-add-uio_dmem_genirq-description-to-uio-documentation.patch b/patches.uio/drivers-uio-add-uio_dmem_genirq-description-to-uio-documentation.patch
new file mode 100644
index 0000000000000..1b1bdc8bf0f20
--- /dev/null
+++ b/patches.uio/drivers-uio-add-uio_dmem_genirq-description-to-uio-documentation.patch
@@ -0,0 +1,84 @@
+From dhobsong@igel.co.jp Wed Nov 21 18:27:15 2012
+From: Damian Hobson-Garcia <dhobsong@igel.co.jp>
+Date: Thu, 22 Nov 2012 11:26:49 +0900
+Subject: [PATCH 2/6] drivers: uio: Add uio_dmem_genirq description to UIO documentation
+To: gregkh@linuxfoundation.org
+Cc: ltsi-dev@lists.linuxfoundation.org, Damian Hobson-Garcia <dhobsong@igel.co.jp>
+Message-ID: <1353551213-17996-3-git-send-email-dhobsong@igel.co.jp>
+
+
+Signed-off-by: Damian Hobson-Garcia <dhobsong@igel.co.jp>
+---
+ Documentation/DocBook/uio-howto.tmpl | 56 ++++++++++++++++++++++++++++++++++
+ 1 files changed, 56 insertions(+), 0 deletions(-)
+
+diff --git a/Documentation/DocBook/uio-howto.tmpl b/Documentation/DocBook/uio-howto.tmpl
+index ac3d001..fdbf86f 100644
+--- a/Documentation/DocBook/uio-howto.tmpl
++++ b/Documentation/DocBook/uio-howto.tmpl
+@@ -719,6 +719,62 @@ framework to set up sysfs files for this region. Simply leave it alone.
+ </para>
+ </sect1>
+
++<sect1 id="using uio_dmem_genirq">
++<title>Using uio_dmem_genirq for platform devices</title>
++ <para>
++ In addition to statically allocated memory ranges, they may also be
++ a desire to use dynamically allocated regions in a user space driver.
++ In particular, being able to access memory made available through the
++ dma-mapping API, may be particularly useful. The
++ <varname>uio_dmem_genirq</varname> driver provides a way to accomplish
++ this.
++ </para>
++ <para>
++ This driver is used in a similar manner to the
++ <varname>"uio_pdrv_genirq"</varname> driver with respect to interrupt
++ configuration and handling.
++ </para>
++ <para>
++ Set the <varname>.name</varname> element of
++ <varname>struct platform_device</varname> to
++ <varname>"uio_dmem_genirq"</varname> to use this driver.
++ </para>
++ <para>
++ When using this driver, fill in the <varname>.platform_data</varname>
++ element of <varname>struct platform_device</varname>, which is of type
++ <varname>struct uio_dmem_genirq_pdata</varname> and which contains the
++ following elements:
++ </para>
++ <itemizedlist>
++ <listitem><varname>struct uio_info uioinfo</varname>: The same
++ structure used as the <varname>uio_pdrv_genirq</varname> platform
++ data</listitem>
++ <listitem><varname>unsigned int *dynamic_region_sizes</varname>:
++ Pointer to list of sizes of dynamic memory regions to be mapped into
++ user space.
++ </listitem>
++ <listitem><varname>unsigned int num_dynamic_regions</varname>:
++ Number of elements in <varname>dynamic_region_sizes</varname> array.
++ </listitem>
++ </itemizedlist>
++ <para>
++ The dynamic regions defined in the platform data will be appended to
++ the <varname> mem[] </varname> array after the platform device
++ resources, which implies that the total number of static and dynamic
++ memory regions cannot exceed <varname>MAX_UIO_MAPS</varname>.
++ </para>
++ <para>
++ The dynamic memory regions will be allocated when the UIO device file,
++ <varname>/dev/uioX</varname> is opened.
++ Simiar to static memory resources, the memory region information for
++ dynamic regions is then visible via sysfs at
++ <varname>/sys/class/uio/uioX/maps/mapY/*</varname>.
++ The dynmaic memory regions will be freed when the UIO device file is
++ closed. When no processes are holding the device file open, the address
++ returned to userspace is DMA_ERROR_CODE.
++ </para>
++</sect1>
++
+ </chapter>
+
+ <chapter id="userspace_driver" xreflabel="Writing a driver in user space">
+--
+1.7.5.4
+
diff --git a/patches.uio/drivers-uio-only-allocate-new-private-data-when-probing-device-tree-node.patch b/patches.uio/drivers-uio-only-allocate-new-private-data-when-probing-device-tree-node.patch
new file mode 100644
index 0000000000000..754df8fdeefa0
--- /dev/null
+++ b/patches.uio/drivers-uio-only-allocate-new-private-data-when-probing-device-tree-node.patch
@@ -0,0 +1,50 @@
+From dhobsong@igel.co.jp Wed Nov 21 18:27:25 2012
+From: Damian Hobson-Garcia <dhobsong@igel.co.jp>
+Date: Thu, 22 Nov 2012 11:26:53 +0900
+Subject: [PATCH 6/6] drivers: uio: Only allocate new private data when probing device tree node
+To: gregkh@linuxfoundation.org
+Cc: ltsi-dev@lists.linuxfoundation.org, Damian Hobson-Garcia <dhobsong@igel.co.jp>
+Message-ID: <1353551213-17996-7-git-send-email-dhobsong@igel.co.jp>
+
+
+The same condition should be used both when allocating and freeing the
+driver private data. When dev.of_node is non NULL, allocate a new
+private data structure, otherwise use the values from the platform data.
+
+Reported-by: Fengguang Wu <fengguang.wu@intel.com>
+
+Signed-off-by: Damian Hobson-Garcia <dhobsong@igel.co.jp>
+---
+ drivers/uio/uio_dmem_genirq.c | 2 +-
+ drivers/uio/uio_pdrv_genirq.c | 2 +-
+ 2 files changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/uio/uio_dmem_genirq.c b/drivers/uio/uio_dmem_genirq.c
+index bbdf925..252434c 100644
+--- a/drivers/uio/uio_dmem_genirq.c
++++ b/drivers/uio/uio_dmem_genirq.c
+@@ -153,7 +153,7 @@ static int uio_dmem_genirq_probe(struct platform_device *pdev)
+ int ret = -EINVAL;
+ int i;
+
+- if (!uioinfo) {
++ if (pdev->dev.of_node) {
+ int irq;
+
+ /* alloc uioinfo for one device */
+diff --git a/drivers/uio/uio_pdrv_genirq.c b/drivers/uio/uio_pdrv_genirq.c
+index b98371d..9c976cb 100644
+--- a/drivers/uio/uio_pdrv_genirq.c
++++ b/drivers/uio/uio_pdrv_genirq.c
+@@ -102,7 +102,7 @@ static int uio_pdrv_genirq_probe(struct platform_device *pdev)
+ int ret = -EINVAL;
+ int i;
+
+- if (!uioinfo) {
++ if (pdev->dev.of_node) {
+ int irq;
+
+ /* alloc uioinfo for one device */
+--
+1.7.5.4
+
diff --git a/patches.uio/drivers-uio_dmem_genirq-allow-partial-success-when-opening-device.patch b/patches.uio/drivers-uio_dmem_genirq-allow-partial-success-when-opening-device.patch
new file mode 100644
index 0000000000000..278d9dc302b45
--- /dev/null
+++ b/patches.uio/drivers-uio_dmem_genirq-allow-partial-success-when-opening-device.patch
@@ -0,0 +1,56 @@
+From dhobsong@igel.co.jp Wed Nov 21 18:27:22 2012
+From: Damian Hobson-Garcia <dhobsong@igel.co.jp>
+Date: Thu, 22 Nov 2012 11:26:52 +0900
+Subject: [PATCH 5/6] drivers: uio_dmem_genirq: Allow partial success when opening device
+To: gregkh@linuxfoundation.org
+Cc: ltsi-dev@lists.linuxfoundation.org, Damian Hobson-Garcia <dhobsong@igel.co.jp>
+Message-ID: <1353551213-17996-6-git-send-email-dhobsong@igel.co.jp>
+
+
+The uio device should not fail on open just because one memory allocation
+fails. The device might export several regions, the failure of some of
+which may or may not be a problem for the user space driver. Failing
+regions will remain unmapped, and successful regions will be mapped and
+exported to user space. Also deals with the case where failing to map
+a region after successfully allocating others would not unmap the
+successfully allocated regions before dying.
+
+Signed-off-by: Damian Hobson-Garcia <dhobsong@igel.co.jp>
+---
+ drivers/uio/uio_dmem_genirq.c | 12 ++++++------
+ 1 files changed, 6 insertions(+), 6 deletions(-)
+
+diff --git a/drivers/uio/uio_dmem_genirq.c b/drivers/uio/uio_dmem_genirq.c
+index 7be8d04..bbdf925 100644
+--- a/drivers/uio/uio_dmem_genirq.c
++++ b/drivers/uio/uio_dmem_genirq.c
+@@ -62,8 +62,6 @@ static int uio_dmem_genirq_open(struct uio_info *info, struct inode *inode)
+ (dma_addr_t *)&uiomem->addr, GFP_KERNEL);
+ if (!addr) {
+ uiomem->addr = DMEM_MAP_ERROR;
+- ret = -ENOMEM;
+- break;
+ }
+ priv->dmem_region_vaddr[dmem_region++] = addr;
+ ++uiomem;
+@@ -93,11 +91,13 @@ static int uio_dmem_genirq_release(struct uio_info *info, struct inode *inode)
+ while (!priv->refcnt && uiomem < &priv->uioinfo->mem[MAX_UIO_MAPS]) {
+ if (!uiomem->size)
+ break;
+-
+- dma_free_coherent(&priv->pdev->dev, uiomem->size,
+- priv->dmem_region_vaddr[dmem_region++],
+- uiomem->addr);
++ if (priv->dmem_region_vaddr[dmem_region]) {
++ dma_free_coherent(&priv->pdev->dev, uiomem->size,
++ priv->dmem_region_vaddr[dmem_region],
++ uiomem->addr);
++ }
+ uiomem->addr = DMEM_MAP_ERROR;
++ ++dmem_region;
+ ++uiomem;
+ }
+
+--
+1.7.5.4
+
diff --git a/patches.uio/drivers-uio_dmem_genirq-don-t-mix-address-spaces-for-dynamic-region-vaddr.patch b/patches.uio/drivers-uio_dmem_genirq-don-t-mix-address-spaces-for-dynamic-region-vaddr.patch
new file mode 100644
index 0000000000000..45683161a11d0
--- /dev/null
+++ b/patches.uio/drivers-uio_dmem_genirq-don-t-mix-address-spaces-for-dynamic-region-vaddr.patch
@@ -0,0 +1,80 @@
+From dhobsong@igel.co.jp Wed Nov 21 18:27:18 2012
+From: Damian Hobson-Garcia <dhobsong@igel.co.jp>
+Date: Thu, 22 Nov 2012 11:26:50 +0900
+Subject: [PATCH 3/6] drivers: uio_dmem_genirq: Don't mix address spaces for dynamic region vaddr
+To: gregkh@linuxfoundation.org
+Cc: ltsi-dev@lists.linuxfoundation.org, Damian Hobson-Garcia <dhobsong@igel.co.jp>
+Message-ID: <1353551213-17996-4-git-send-email-dhobsong@igel.co.jp>
+
+
+Assigning the virtual address returned from dma_alloc_coherent to the the
+internal_addr element of uioinfo produces the following sparse errors since
+internal_addr is a void __iomem * and dma_alloc_coherent returns void *.
+
++ drivers/uio/uio_dmem_genirq.c:65:39: sparse: incorrect type in assignment (different address spaces)
+drivers/uio/uio_dmem_genirq.c:65:39: expected void [noderef] <asn:2>*internal_addr
+drivers/uio/uio_dmem_genirq.c:65:39: got void *[assigned] addr
++ drivers/uio/uio_dmem_genirq.c:93:17: sparse: incorrect type in argument 3 (different address spaces)
+drivers/uio/uio_dmem_genirq.c:93:17: expected void *vaddr
+drivers/uio/uio_dmem_genirq.c:93:17: got void [noderef] <asn:2>*internal_addr
+
+Store the void * in the driver's private data instead.
+
+Reported-by: Fengguang Wu <fengguang.wu@intel.com>
+
+Signed-off-by: Damian Hobson-Garcia <dhobsong@igel.co.jp>
+---
+ drivers/uio/uio_dmem_genirq.c | 9 ++++++---
+ 1 files changed, 6 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/uio/uio_dmem_genirq.c b/drivers/uio/uio_dmem_genirq.c
+index 4d4dd00..d8bbe07 100644
+--- a/drivers/uio/uio_dmem_genirq.c
++++ b/drivers/uio/uio_dmem_genirq.c
+@@ -37,6 +37,7 @@ struct uio_dmem_genirq_platdata {
+ struct platform_device *pdev;
+ unsigned int dmem_region_start;
+ unsigned int num_dmem_regions;
++ void *dmem_region_vaddr[MAX_UIO_MAPS];
+ struct mutex alloc_lock;
+ unsigned int refcnt;
+ };
+@@ -46,6 +47,7 @@ static int uio_dmem_genirq_open(struct uio_info *info, struct inode *inode)
+ struct uio_dmem_genirq_platdata *priv = info->priv;
+ struct uio_mem *uiomem;
+ int ret = 0;
++ int dmem_region = priv->dmem_region_start;
+
+ uiomem = &priv->uioinfo->mem[priv->dmem_region_start];
+
+@@ -61,8 +63,7 @@ static int uio_dmem_genirq_open(struct uio_info *info, struct inode *inode)
+ ret = -ENOMEM;
+ break;
+ }
+-
+- uiomem->internal_addr = addr;
++ priv->dmem_region_vaddr[dmem_region++] = addr;
+ ++uiomem;
+ }
+ priv->refcnt++;
+@@ -77,6 +78,7 @@ static int uio_dmem_genirq_release(struct uio_info *info, struct inode *inode)
+ {
+ struct uio_dmem_genirq_platdata *priv = info->priv;
+ struct uio_mem *uiomem;
++ int dmem_region = priv->dmem_region_start;
+
+ /* Tell the Runtime PM code that the device has become idle */
+ pm_runtime_put_sync(&priv->pdev->dev);
+@@ -91,7 +93,8 @@ static int uio_dmem_genirq_release(struct uio_info *info, struct inode *inode)
+ break;
+
+ dma_free_coherent(&priv->pdev->dev, uiomem->size,
+- uiomem->internal_addr, uiomem->addr);
++ priv->dmem_region_vaddr[dmem_region++],
++ uiomem->addr);
+ uiomem->addr = DMA_ERROR_CODE;
+ ++uiomem;
+ }
+--
+1.7.5.4
+
diff --git a/patches.uio/drivers-uio_dmem_genirq-don-t-use-dma_error_code-to-indicate-unmapped-regions.patch b/patches.uio/drivers-uio_dmem_genirq-don-t-use-dma_error_code-to-indicate-unmapped-regions.patch
new file mode 100644
index 0000000000000..81b3e22bc4d93
--- /dev/null
+++ b/patches.uio/drivers-uio_dmem_genirq-don-t-use-dma_error_code-to-indicate-unmapped-regions.patch
@@ -0,0 +1,75 @@
+From dhobsong@igel.co.jp Wed Nov 21 18:27:20 2012
+From: Damian Hobson-Garcia <dhobsong@igel.co.jp>
+Date: Thu, 22 Nov 2012 11:26:51 +0900
+Subject: [PATCH 4/6] drivers: uio_dmem_genirq: Don't use DMA_ERROR_CODE to indicate unmapped regions
+To: gregkh@linuxfoundation.org
+Cc: ltsi-dev@lists.linuxfoundation.org, Damian Hobson-Garcia <dhobsong@igel.co.jp>
+Message-ID: <1353551213-17996-5-git-send-email-dhobsong@igel.co.jp>
+
+
+DMA_ERROR_CODE is not defined on all architectures and is architecture
+specific. Instead, use the constant, ~0 to indicate unmapped regions.
+
+Reported-by: Fengguang Wu <fengguang.wu@intel.com>
+Reported-by: Geert Uytterhoeven <geert@linux-m68k.org>
+
+Signed-off-by: Damian Hobson-Garcia <dhobsong@igel.co.jp>
+---
+ Documentation/DocBook/uio-howto.tmpl | 2 +-
+ drivers/uio/uio_dmem_genirq.c | 6 ++++--
+ 2 files changed, 5 insertions(+), 3 deletions(-)
+
+diff --git a/Documentation/DocBook/uio-howto.tmpl b/Documentation/DocBook/uio-howto.tmpl
+index fdbf86f..ddb05e9 100644
+--- a/Documentation/DocBook/uio-howto.tmpl
++++ b/Documentation/DocBook/uio-howto.tmpl
+@@ -771,7 +771,7 @@ framework to set up sysfs files for this region. Simply leave it alone.
+ <varname>/sys/class/uio/uioX/maps/mapY/*</varname>.
+ The dynmaic memory regions will be freed when the UIO device file is
+ closed. When no processes are holding the device file open, the address
+- returned to userspace is DMA_ERROR_CODE.
++ returned to userspace is ~0.
+ </para>
+ </sect1>
+
+diff --git a/drivers/uio/uio_dmem_genirq.c b/drivers/uio/uio_dmem_genirq.c
+index d8bbe07..7be8d04 100644
+--- a/drivers/uio/uio_dmem_genirq.c
++++ b/drivers/uio/uio_dmem_genirq.c
+@@ -29,6 +29,7 @@
+ #include <linux/of_address.h>
+
+ #define DRIVER_NAME "uio_dmem_genirq"
++#define DMEM_MAP_ERROR (~0)
+
+ struct uio_dmem_genirq_platdata {
+ struct uio_info *uioinfo;
+@@ -60,6 +61,7 @@ static int uio_dmem_genirq_open(struct uio_info *info, struct inode *inode)
+ addr = dma_alloc_coherent(&priv->pdev->dev, uiomem->size,
+ (dma_addr_t *)&uiomem->addr, GFP_KERNEL);
+ if (!addr) {
++ uiomem->addr = DMEM_MAP_ERROR;
+ ret = -ENOMEM;
+ break;
+ }
+@@ -95,7 +97,7 @@ static int uio_dmem_genirq_release(struct uio_info *info, struct inode *inode)
+ dma_free_coherent(&priv->pdev->dev, uiomem->size,
+ priv->dmem_region_vaddr[dmem_region++],
+ uiomem->addr);
+- uiomem->addr = DMA_ERROR_CODE;
++ uiomem->addr = DMEM_MAP_ERROR;
+ ++uiomem;
+ }
+
+@@ -238,7 +240,7 @@ static int uio_dmem_genirq_probe(struct platform_device *pdev)
+ break;
+ }
+ uiomem->memtype = UIO_MEM_PHYS;
+- uiomem->addr = DMA_ERROR_CODE;
++ uiomem->addr = DMEM_MAP_ERROR;
+ uiomem->size = pdata->dynamic_region_sizes[i];
+ ++uiomem;
+ }
+--
+1.7.5.4
+
diff --git a/series b/series
index c5196b915c2d1..e6bec868d4810 100644
--- a/series
+++ b/series
@@ -733,3 +733,13 @@ patches.kzm9d/mach-shmobile-use-dt_machine-for-kzm9d-v3.patch
patches.kzm9d/arm-mach-shmobile-fix-build-when-smp-is-enabled-and-emev2-is-not-enabled.patch
+#############################################################################
+# UIO driver patches
+#
+patches.uio/drivers-uio-add-new-uio-device-for-dynamic-memory-allocation.patch
+patches.uio/drivers-uio-add-uio_dmem_genirq-description-to-uio-documentation.patch
+patches.uio/drivers-uio_dmem_genirq-don-t-mix-address-spaces-for-dynamic-region-vaddr.patch
+patches.uio/drivers-uio_dmem_genirq-don-t-use-dma_error_code-to-indicate-unmapped-regions.patch
+patches.uio/drivers-uio_dmem_genirq-allow-partial-success-when-opening-device.patch
+patches.uio/drivers-uio-only-allocate-new-private-data-when-probing-device-tree-node.patch
+