UIO driver patches added
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 5 Dec 2012 22:29:48 +0000 (14:29 -0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 5 Dec 2012 22:29:48 +0000 (14:29 -0800)
patches.uio/drivers-uio-add-new-uio-device-for-dynamic-memory-allocation.patch [new file with mode: 0644]
patches.uio/drivers-uio-add-uio_dmem_genirq-description-to-uio-documentation.patch [new file with mode: 0644]
patches.uio/drivers-uio-only-allocate-new-private-data-when-probing-device-tree-node.patch [new file with mode: 0644]
patches.uio/drivers-uio_dmem_genirq-allow-partial-success-when-opening-device.patch [new file with mode: 0644]
patches.uio/drivers-uio_dmem_genirq-don-t-mix-address-spaces-for-dynamic-region-vaddr.patch [new file with mode: 0644]
patches.uio/drivers-uio_dmem_genirq-don-t-use-dma_error_code-to-indicate-unmapped-regions.patch [new file with mode: 0644]
series

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 (file)
index 0000000..1fd4f90
--- /dev/null
@@ -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 (file)
index 0000000..1b1bdc8
--- /dev/null
@@ -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 (file)
index 0000000..754df8f
--- /dev/null
@@ -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 (file)
index 0000000..278d9dc
--- /dev/null
@@ -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 (file)
index 0000000..4568316
--- /dev/null
@@ -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 (file)
index 0000000..81b3e22
--- /dev/null
@@ -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 c5196b9..e6bec86 100644 (file)
--- 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
+