diff options
author | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2012-12-05 14:29:48 -0800 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2012-12-05 14:29:48 -0800 |
commit | c8c7af548d05efd37a5a7d14e2019317bca9bdf8 (patch) | |
tree | e2a32f555653ea967fbcef67ceac35209fe20374 | |
parent | 8f2828f9846021088223c43284d12b8ba0eac0d0 (diff) | |
download | ltsi-kernel-c8c7af548d05efd37a5a7d14e2019317bca9bdf8.tar.gz |
UIO driver patches added
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 + @@ -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 + |